diff options
304 files changed, 5909 insertions, 3222 deletions
diff --git a/Android.bp b/Android.bp index eabd9c7565da..cf73451d444e 100644 --- a/Android.bp +++ b/Android.bp @@ -255,7 +255,7 @@ java_library { "android.hardware.vibrator-V1.1-java", "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", - "android.hardware.vibrator-V2-java", + "android.hardware.vibrator-V3-java", "android.se.omapi-V1-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 36a335e97d33..fd0262ef5771 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -409,6 +409,7 @@ package android { field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String USE_ON_DEVICE_INTELLIGENCE = "android.permission.USE_ON_DEVICE_INTELLIGENCE"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED"; + field @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final String VIBRATE_VENDOR_EFFECTS = "android.permission.VIBRATE_VENDOR_EFFECTS"; field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"; @@ -11354,6 +11355,10 @@ package android.os { field @NonNull public static final android.os.Parcelable.Creator<android.os.UserManager.EnforcingUser> CREATOR; } + public abstract class VibrationEffect implements android.os.Parcelable { + method @FlaggedApi("android.os.vibrator.vendor_vibration_effects") @NonNull @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS) public static android.os.VibrationEffect createVendorEffect(@NonNull android.os.PersistableBundle); + } + public abstract class Vibrator { method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 88b5275d37f8..90af25984e6b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2577,6 +2577,16 @@ package android.os { public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException { } + @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect { + method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull(); + method public long getDuration(); + method public int getEffectStrength(); + method public float getLinearScale(); + method @NonNull public android.os.PersistableBundle getVendorData(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR; + } + public static class VibrationEffect.VibrationParameter { method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float); method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index cbabb0227763..90de7abf845c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2214,6 +2214,9 @@ public class Activity extends ContextThemeWrapper notifyVoiceInteractionManagerServiceActivityEvent( VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME); + // Notify autofill + getAutofillClientController().onActivityPostResumed(); + mCalled = true; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4952af353670..75aab7d0fc63 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3818,8 +3818,7 @@ public final class ActivityThread extends ClientTransactionHandler final ArrayList<ResultInfo> list = new ArrayList<>(); list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken)); final ClientTransaction clientTransaction = new ClientTransaction(mAppThread); - final ActivityResultItem activityResultItem = ActivityResultItem.obtain( - activityToken, list); + final ActivityResultItem activityResultItem = new ActivityResultItem(activityToken, list); clientTransaction.addTransactionItem(activityResultItem); try { mAppThread.scheduleTransaction(clientTransaction); @@ -4614,7 +4613,7 @@ public final class ActivityThread extends ClientTransactionHandler private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { final ClientTransaction transaction = new ClientTransaction(mAppThread); - final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, + final PauseActivityItem pauseActivityItem = new PauseActivityItem(r.token, r.activity.isFinishing(), /* userLeaving */ true, /* dontReport */ false, /* autoEnteringPip */ false); transaction.addTransactionItem(pauseActivityItem); @@ -4623,7 +4622,7 @@ public final class ActivityThread extends ClientTransactionHandler private void scheduleResume(ActivityClientRecord r) { final ClientTransaction transaction = new ClientTransaction(mAppThread); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token, + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(r.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); transaction.addTransactionItem(resumeActivityItem); executeTransaction(transaction); @@ -6239,7 +6238,7 @@ public final class ActivityThread extends ClientTransactionHandler r.createdConfig != null ? r.createdConfig : mConfigurationController.getConfiguration(), r.overrideConfig); - final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( + final ActivityRelaunchItem activityRelaunchItem = new ActivityRelaunchItem( r.token, null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow, r.getActivityWindowInfo()); diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index 11d7ff86ce3d..2b52681d1be8 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -19,6 +19,8 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.INVALID_DISPLAY; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; @@ -34,12 +36,23 @@ import java.util.Objects; /** * Activity configuration changed callback. + * * @hide */ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { - private Configuration mConfiguration; - private ActivityWindowInfo mActivityWindowInfo; + @NonNull + private final Configuration mConfiguration; + + @NonNull + private final ActivityWindowInfo mActivityWindowInfo; + + public ActivityConfigurationChangeItem(@NonNull IBinder activityToken, + @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) { + super(activityToken); + mConfiguration = new Configuration(config); + mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); + } @Override public void preExecute(@NonNull ClientTransactionHandler client) { @@ -59,38 +72,9 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } - // ObjectPoolItem implementation - - private ActivityConfigurationChangeItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken, - @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) { - ActivityConfigurationChangeItem instance = - ObjectPool.obtain(ActivityConfigurationChangeItem.class); - if (instance == null) { - instance = new ActivityConfigurationChangeItem(); - } - instance.setActivityToken(activityToken); - instance.mConfiguration = new Configuration(config); - instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mConfiguration = null; - mActivityWindowInfo = null; - ObjectPool.recycle(this); - } - - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -98,11 +82,11 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { dest.writeTypedObject(mActivityWindowInfo, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private ActivityConfigurationChangeItem(@NonNull Parcel in) { super(in); - mConfiguration = in.readTypedObject(Configuration.CREATOR); - mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR); + mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR)); + mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR)); } public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR = diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java index d07ad46b0f24..778f13470f4b 100644 --- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java +++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java @@ -58,12 +58,6 @@ public abstract class ActivityLifecycleItem extends ActivityTransactionItem { super(activityToken); } - // TODO(b/311089192): Remove this method once no subclasses obtain from the object pool. - @Deprecated - ActivityLifecycleItem() { - super(); - } - // Parcelable implementation /** Reads from Parcel. */ diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java index 45bf235de2cd..cecf7013c79c 100644 --- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java +++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java @@ -18,6 +18,8 @@ package android.app.servertransaction; import static android.app.ActivityThread.DEBUG_ORDER; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; @@ -39,25 +41,50 @@ import java.util.Objects; /** * Activity relaunch callback. + * * @hide */ public class ActivityRelaunchItem extends ActivityTransactionItem { private static final String TAG = "ActivityRelaunchItem"; - private List<ResultInfo> mPendingResults; - private List<ReferrerIntent> mPendingNewIntents; - private int mConfigChanges; - private MergedConfiguration mConfig; - private boolean mPreserveWindow; - private ActivityWindowInfo mActivityWindowInfo; + @Nullable + private final List<ResultInfo> mPendingResults; + + @Nullable + private final List<ReferrerIntent> mPendingNewIntents; + + @NonNull + private final MergedConfiguration mConfig; + + @NonNull + private final ActivityWindowInfo mActivityWindowInfo; + + private final int mConfigChanges; + private final boolean mPreserveWindow; /** * A record that was properly configured for relaunch. Execution will be cancelled if not * initialized after {@link #preExecute(ClientTransactionHandler)}. */ + @Nullable private ActivityClientRecord mActivityClientRecord; + public ActivityRelaunchItem(@NonNull IBinder activityToken, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, + @NonNull MergedConfiguration config, boolean preserveWindow, + @NonNull ActivityWindowInfo activityWindowInfo) { + super(activityToken); + mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null; + mPendingNewIntents = + pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null; + mConfig = new MergedConfiguration(config); + mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); + mConfigChanges = configChanges; + mPreserveWindow = preserveWindow; + } + @Override public void preExecute(@NonNull ClientTransactionHandler client) { // The local config is already scaled so only apply if this item is from server side. @@ -87,70 +114,29 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { client.reportRelaunch(r); } - // ObjectPoolItem implementation - - private ActivityRelaunchItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static ActivityRelaunchItem obtain(@NonNull IBinder activityToken, - @Nullable List<ResultInfo> pendingResults, - @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, - @NonNull MergedConfiguration config, boolean preserveWindow, - @NonNull ActivityWindowInfo activityWindowInfo) { - ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class); - if (instance == null) { - instance = new ActivityRelaunchItem(); - } - instance.setActivityToken(activityToken); - instance.mPendingResults = pendingResults != null ? new ArrayList<>(pendingResults) : null; - instance.mPendingNewIntents = - pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null; - instance.mConfigChanges = configChanges; - instance.mConfig = new MergedConfiguration(config); - instance.mPreserveWindow = preserveWindow; - instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mPendingResults = null; - mPendingNewIntents = null; - mConfigChanges = 0; - mConfig = null; - mPreserveWindow = false; - mActivityClientRecord = null; - mActivityWindowInfo = null; - ObjectPool.recycle(this); - } - - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeTypedList(mPendingResults, flags); dest.writeTypedList(mPendingNewIntents, flags); - dest.writeInt(mConfigChanges); dest.writeTypedObject(mConfig, flags); - dest.writeBoolean(mPreserveWindow); dest.writeTypedObject(mActivityWindowInfo, flags); + dest.writeInt(mConfigChanges); + dest.writeBoolean(mPreserveWindow); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private ActivityRelaunchItem(@NonNull Parcel in) { super(in); mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR); mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); + mConfig = requireNonNull(in.readTypedObject(MergedConfiguration.CREATOR)); + mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR)); mConfigChanges = in.readInt(); - mConfig = in.readTypedObject(MergedConfiguration.CREATOR); mPreserveWindow = in.readBoolean(); - mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR); } public static final @NonNull Creator<ActivityRelaunchItem> CREATOR = @@ -175,9 +161,10 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { final ActivityRelaunchItem other = (ActivityRelaunchItem) o; return Objects.equals(mPendingResults, other.mPendingResults) && Objects.equals(mPendingNewIntents, other.mPendingNewIntents) - && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig) - && mPreserveWindow == other.mPreserveWindow - && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo); + && Objects.equals(mConfig, other.mConfig) + && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo) + && mConfigChanges == other.mConfigChanges + && mPreserveWindow == other.mPreserveWindow; } @Override @@ -186,10 +173,10 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { result = 31 * result + super.hashCode(); result = 31 * result + Objects.hashCode(mPendingResults); result = 31 * result + Objects.hashCode(mPendingNewIntents); - result = 31 * result + mConfigChanges; result = 31 * result + Objects.hashCode(mConfig); - result = 31 * result + (mPreserveWindow ? 1 : 0); result = 31 * result + Objects.hashCode(mActivityWindowInfo); + result = 31 * result + mConfigChanges; + result = 31 * result + (mPreserveWindow ? 1 : 0); return result; } @@ -198,9 +185,9 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { return "ActivityRelaunchItem{" + super.toString() + ",pendingResults=" + mPendingResults + ",pendingNewIntents=" + mPendingNewIntents - + ",configChanges=" + mConfigChanges + ",config=" + mConfig - + ",preserveWindow=" + mPreserveWindow - + ",activityWindowInfo=" + mActivityWindowInfo + "}"; + + ",activityWindowInfo=" + mActivityWindowInfo + + ",configChanges=" + mConfigChanges + + ",preserveWindow=" + mPreserveWindow + "}"; } } diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index 51a09fb59236..761c519d2105 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -41,10 +41,13 @@ import java.util.Objects; /** * Activity result delivery callback. + * * @hide */ public class ActivityResultItem extends ActivityTransactionItem { + // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed. + // We cannot do it now to avoid app compatibility regression. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private List<ResultInfo> mResultInfoList; @@ -56,6 +59,12 @@ public class ActivityResultItem extends ActivityTransactionItem { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) public static final long CALL_ACTIVITY_RESULT_BEFORE_RESUME = 78294732L; + public ActivityResultItem(@NonNull IBinder activityToken, + @NonNull List<ResultInfo> resultInfoList) { + super(activityToken); + mResultInfoList = new ArrayList<>(resultInfoList); + } + @Override public int getPostExecutionState() { return CompatChanges.isChangeEnabled(CALL_ACTIVITY_RESULT_BEFORE_RESUME) @@ -70,43 +79,19 @@ public class ActivityResultItem extends ActivityTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } - // ObjectPoolItem implementation - - private ActivityResultItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static ActivityResultItem obtain(@NonNull IBinder activityToken, - @NonNull List<ResultInfo> resultInfoList) { - ActivityResultItem instance = ObjectPool.obtain(ActivityResultItem.class); - if (instance == null) { - instance = new ActivityResultItem(); - } - instance.setActivityToken(activityToken); - instance.mResultInfoList = new ArrayList<>(resultInfoList); - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mResultInfoList = null; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeTypedList(mResultInfoList, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private ActivityResultItem(@NonNull Parcel in) { super(in); + // TODO(b/170729553): Wrap with requireNonNull once @UnsupportedAppUsage removed. mResultInfoList = in.createTypedArrayList(ResultInfo.CREATOR); } diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java index 8ef86ee63c54..506a158b4d47 100644 --- a/core/java/android/app/servertransaction/ActivityTransactionItem.java +++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java @@ -49,30 +49,13 @@ import java.util.Objects; public abstract class ActivityTransactionItem extends ClientTransactionItem { /** Target client activity. */ - // TODO(b/311089192): Mark this with @NonNull and final. - private IBinder mActivityToken; + @NonNull + private final IBinder mActivityToken; public ActivityTransactionItem(@NonNull IBinder activityToken) { mActivityToken = requireNonNull(activityToken); } - // TODO(b/311089192): Remove this method once no subclasses obtain from the object pool. - @Deprecated - ActivityTransactionItem() { - } - - /** - * Sets the activity token after the instance is obtained from the object pool. - * - * @deprecated This method is deprecated. The object pool is no longer used. - * Instead, directly create a new object with the activity token. - * TODO(b/311089192): Remove this method once no subclasses obtain from the object pool. - */ - @Deprecated - void setActivityToken(@NonNull IBinder activityToken) { - mActivityToken = requireNonNull(activityToken); - } - @Override public final void execute(@NonNull ClientTransactionHandler client, @NonNull PendingTransactionActions pendingActions) { diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java index f2751752abd8..bcbb51480821 100644 --- a/core/java/android/app/servertransaction/BaseClientRequest.java +++ b/core/java/android/app/servertransaction/BaseClientRequest.java @@ -22,31 +22,34 @@ import android.app.ClientTransactionHandler; /** * Base interface for individual requests from server to client. * Each of them can be prepared before scheduling and, eventually, executed. + * * @hide */ -public interface BaseClientRequest extends ObjectPoolItem { +public interface BaseClientRequest { /** * Prepares the client request before scheduling. * An example of this might be informing about pending updates for some values. * - * @param client Target client handler. + * @param client target client handler. */ default void preExecute(@NonNull ClientTransactionHandler client) { } /** * Executes the request. - * @param client Target client handler. - * @param pendingActions Container that may have data pending to be used. + * + * @param client target client handler. + * @param pendingActions container that may have data pending to be used. */ void execute(@NonNull ClientTransactionHandler client, @NonNull PendingTransactionActions pendingActions); /** * Performs all actions that need to happen after execution, e.g. report the result to server. - * @param client Target client handler. - * @param pendingActions Container that may have data pending to be used. + * + * @param client target client handler. + * @param pendingActions container that may have data pending to be used. */ default void postExecute(@NonNull ClientTransactionHandler client, @NonNull PendingTransactionActions pendingActions) { diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java index 2d35bf104657..99ebe1b975a4 100644 --- a/core/java/android/app/servertransaction/ClientTransactionItem.java +++ b/core/java/android/app/servertransaction/ClientTransactionItem.java @@ -75,17 +75,6 @@ public abstract class ClientTransactionItem implements BaseClientRequest, Parcel pw.append(prefix).println(this); } - /** - * Provides a default empty implementation for progressive cleanup. - * - * @deprecated This method is deprecated. The object pool is no longer used, so there's - * no need to recycle objects. - * TODO(b/311089192): Remove once ObjectPoolItem inheritance is removed. - */ - @Override - @Deprecated - public void recycle() {} - // Parcelable @Override diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java index 22da706cc7f4..123d7926160c 100644 --- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java @@ -16,6 +16,8 @@ package android.app.servertransaction; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ClientTransactionHandler; @@ -27,12 +29,20 @@ import java.util.Objects; /** * App configuration change message. + * * @hide */ public class ConfigurationChangeItem extends ClientTransactionItem { - private Configuration mConfiguration; - private int mDeviceId; + @NonNull + private final Configuration mConfiguration; + + private final int mDeviceId; + + public ConfigurationChangeItem(@NonNull Configuration config, int deviceId) { + mConfiguration = new Configuration(config); + mDeviceId = deviceId; + } @Override public void preExecute(@NonNull ClientTransactionHandler client) { @@ -46,55 +56,31 @@ public class ConfigurationChangeItem extends ClientTransactionItem { client.handleConfigurationChanged(mConfiguration, mDeviceId); } - // ObjectPoolItem implementation - - private ConfigurationChangeItem() {} - - /** Obtain an instance initialized with provided params. */ - public static ConfigurationChangeItem obtain(@NonNull Configuration config, int deviceId) { - ConfigurationChangeItem instance = ObjectPool.obtain(ConfigurationChangeItem.class); - if (instance == null) { - instance = new ConfigurationChangeItem(); - } - instance.mConfiguration = new Configuration(config); - instance.mDeviceId = deviceId; - - return instance; - } - - @Override - public void recycle() { - mConfiguration = null; - mDeviceId = 0; - ObjectPool.recycle(this); - } - - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeTypedObject(mConfiguration, flags); dest.writeInt(mDeviceId); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private ConfigurationChangeItem(Parcel in) { - mConfiguration = in.readTypedObject(Configuration.CREATOR); + mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR)); mDeviceId = in.readInt(); } public static final @android.annotation.NonNull Creator<ConfigurationChangeItem> CREATOR = - new Creator<ConfigurationChangeItem>() { - public ConfigurationChangeItem createFromParcel(Parcel in) { - return new ConfigurationChangeItem(in); - } - - public ConfigurationChangeItem[] newArray(int size) { - return new ConfigurationChangeItem[size]; - } - }; + new Creator<>() { + public ConfigurationChangeItem createFromParcel(Parcel in) { + return new ConfigurationChangeItem(in); + } + + public ConfigurationChangeItem[] newArray(int size) { + return new ConfigurationChangeItem[size]; + } + }; @Override public boolean equals(@Nullable Object o) { diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java index 743653f4896e..b6f8655f8bb8 100644 --- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java +++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java @@ -24,39 +24,24 @@ import android.os.Parcel; /** * Request an activity to enter picture-in-picture mode. + * * @hide */ public final class EnterPipRequestedItem extends ActivityTransactionItem { + public EnterPipRequestedItem(@NonNull IBinder activityToken) { + super(activityToken); + } + @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @NonNull PendingTransactionActions pendingActions) { client.handlePictureInPictureRequested(r); } - // ObjectPoolItem implementation - - private EnterPipRequestedItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static EnterPipRequestedItem obtain(@NonNull IBinder activityToken) { - EnterPipRequestedItem instance = ObjectPool.obtain(EnterPipRequestedItem.class); - if (instance == null) { - instance = new EnterPipRequestedItem(); - } - instance.setActivityToken(activityToken); - return instance; - } - - @Override - public void recycle() { - super.recycle(); - ObjectPool.recycle(this); - } - // Parcelable implementation + /** Reads from Parcel. */ private EnterPipRequestedItem(@NonNull Parcel in) { super(in); } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 7819e1ef94c6..235a9f7aeb4c 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -20,9 +20,12 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityClient; +import android.app.ActivityManager.ProcessState; import android.app.ActivityOptions.SceneTransitionInfo; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; @@ -52,41 +55,148 @@ import java.util.Objects; /** * Request to launch an activity. + * * @hide */ public class LaunchActivityItem extends ClientTransactionItem { - private IBinder mActivityToken; + @NonNull + private final IBinder mActivityToken; + + // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed. + // We cannot do it now to avoid app compatibility regression. @UnsupportedAppUsage private Intent mIntent; - private int mIdent; + + // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed. + // We cannot do it now to avoid app compatibility regression. @UnsupportedAppUsage private ActivityInfo mInfo; - private Configuration mCurConfig; - private Configuration mOverrideConfig; - private int mDeviceId; - private String mReferrer; - private IVoiceInteractor mVoiceInteractor; - private int mProcState; - private Bundle mState; - private PersistableBundle mPersistentState; - private List<ResultInfo> mPendingResults; - private List<ReferrerIntent> mPendingNewIntents; - private SceneTransitionInfo mSceneTransitionInfo; - private boolean mIsForward; - private ProfilerInfo mProfilerInfo; - private IBinder mAssistToken; - private IBinder mShareableActivityToken; - private boolean mLaunchedFromBubble; - private IBinder mTaskFragmentToken; - private IBinder mInitialCallerInfoAccessToken; - private ActivityWindowInfo mActivityWindowInfo; + + @NonNull + private final Configuration mCurConfig; + + @NonNull + private final Configuration mOverrideConfig; + + @Nullable + private final String mReferrer; + + @Nullable + private final IVoiceInteractor mVoiceInteractor; + + @Nullable + private final Bundle mState; + + @Nullable + private final PersistableBundle mPersistentState; + + @Nullable + private final List<ResultInfo> mPendingResults; + + @Nullable + private final List<ReferrerIntent> mPendingNewIntents; + + @Nullable + private final SceneTransitionInfo mSceneTransitionInfo; + + @Nullable + private final ProfilerInfo mProfilerInfo; + + @NonNull + private final IBinder mAssistToken; + + @NonNull + private final IBinder mShareableActivityToken; + + @Nullable + private final IBinder mTaskFragmentToken; + + @NonNull + private final IBinder mInitialCallerInfoAccessToken; + + @NonNull + private final ActivityWindowInfo mActivityWindowInfo; /** * It is only non-null if the process is the first time to launch activity. It is only an * optimization for quick look up of the interface so the field is ignored for comparison. */ - private IActivityClientController mActivityClientController; + @Nullable + private final IActivityClientController mActivityClientController; + + private final int mIdent; + private final int mDeviceId; + private final int mProcState; + private final boolean mIsForward; + private final boolean mLaunchedFromBubble; + + public LaunchActivityItem(@NonNull IBinder activityToken, @NonNull Intent intent, + int ident, @NonNull ActivityInfo info, @NonNull Configuration curConfig, + @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer, + @Nullable IVoiceInteractor voiceInteractor, @ProcessState int procState, + @Nullable Bundle state, @Nullable PersistableBundle persistentState, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingNewIntents, + @Nullable SceneTransitionInfo sceneTransitionInfo, + boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken, + @Nullable IActivityClientController activityClientController, + @NonNull IBinder shareableActivityToken, boolean launchedFromBubble, + @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken, + @NonNull ActivityWindowInfo activityWindowInfo) { + this(activityToken, ident, new Configuration(curConfig), new Configuration(overrideConfig), + deviceId, referrer, voiceInteractor, procState, + state != null ? new Bundle(state) : null, + persistentState != null ? new PersistableBundle(persistentState) : null, + pendingResults != null ? new ArrayList<>(pendingResults) : null, + pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null, + sceneTransitionInfo, isForward, + profilerInfo != null ? new ProfilerInfo(profilerInfo) : null, + assistToken, activityClientController, shareableActivityToken, launchedFromBubble, + taskFragmentToken, initialCallerInfoAccessToken, + new ActivityWindowInfo(activityWindowInfo)); + mIntent = new Intent(intent); + mInfo = new ActivityInfo(info); + } + + // TODO(b/170729553): Merge this constructor with previous one if no @UnsupportedAppUsage filed. + // We cannot do it now to avoid app compatibility regression. + private LaunchActivityItem(@NonNull IBinder activityToken, int ident, + @NonNull Configuration curConfig, + @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer, + @Nullable IVoiceInteractor voiceInteractor, @ProcessState int procState, + @Nullable Bundle state, @Nullable PersistableBundle persistentState, + @Nullable List<ResultInfo> pendingResults, + @Nullable List<ReferrerIntent> pendingNewIntents, + @Nullable SceneTransitionInfo sceneTransitionInfo, + boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken, + @Nullable IActivityClientController activityClientController, + @NonNull IBinder shareableActivityToken, boolean launchedFromBubble, + @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken, + @NonNull ActivityWindowInfo activityWindowInfo) { + mActivityToken = activityToken; + mIdent = ident; + mCurConfig = curConfig; + mOverrideConfig = overrideConfig; + mDeviceId = deviceId; + mReferrer = referrer; + mVoiceInteractor = voiceInteractor; + mProcState = procState; + mState = state; + mPersistentState = persistentState; + mPendingResults = pendingResults; + mPendingNewIntents = pendingNewIntents; + mSceneTransitionInfo = sceneTransitionInfo; + mIsForward = isForward; + mProfilerInfo = profilerInfo; + mAssistToken = assistToken; + mActivityClientController = activityClientController; + mShareableActivityToken = shareableActivityToken; + mLaunchedFromBubble = launchedFromBubble; + mTaskFragmentToken = taskFragmentToken; + mInitialCallerInfoAccessToken = initialCallerInfoAccessToken; + mActivityWindowInfo = activityWindowInfo; + } @Override public void preExecute(@NonNull ClientTransactionHandler client) { @@ -119,44 +229,6 @@ public class LaunchActivityItem extends ClientTransactionItem { client.countLaunchingActivities(-1); } - // ObjectPoolItem implementation - - private LaunchActivityItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static LaunchActivityItem obtain(@NonNull IBinder activityToken, @NonNull Intent intent, - int ident, @NonNull ActivityInfo info, @NonNull Configuration curConfig, - @NonNull Configuration overrideConfig, int deviceId, @Nullable String referrer, - @Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state, - @Nullable PersistableBundle persistentState, @Nullable List<ResultInfo> pendingResults, - @Nullable List<ReferrerIntent> pendingNewIntents, - @Nullable SceneTransitionInfo sceneTransitionInfo, - boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken, - @Nullable IActivityClientController activityClientController, - @NonNull IBinder shareableActivityToken, boolean launchedFromBubble, - @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken, - @NonNull ActivityWindowInfo activityWindowInfo) { - LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); - if (instance == null) { - instance = new LaunchActivityItem(); - } - setValues(instance, activityToken, new Intent(intent), ident, new ActivityInfo(info), - new Configuration(curConfig), new Configuration(overrideConfig), deviceId, - referrer, voiceInteractor, procState, - state != null ? new Bundle(state) : null, - persistentState != null ? new PersistableBundle(persistentState) : null, - pendingResults != null ? new ArrayList<>(pendingResults) : null, - pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null, - sceneTransitionInfo, isForward, - profilerInfo != null ? new ProfilerInfo(profilerInfo) : null, - assistToken, activityClientController, shareableActivityToken, - launchedFromBubble, taskFragmentToken, initialCallerInfoAccessToken, - new ActivityWindowInfo(activityWindowInfo)); - - return instance; - } - @VisibleForTesting(visibility = PACKAGE) @NonNull @Override @@ -164,22 +236,13 @@ public class LaunchActivityItem extends ClientTransactionItem { return mActivityToken; } - @Override - public void recycle() { - setValues(this, null, null, 0, null, null, null, 0, null, null, 0, null, null, null, null, - null, false, null, null, null, null, false, null, null, null); - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write from Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeStrongBinder(mActivityToken); - dest.writeTypedObject(mIntent, flags); dest.writeInt(mIdent); - dest.writeTypedObject(mInfo, flags); dest.writeTypedObject(mCurConfig, flags); dest.writeTypedObject(mOverrideConfig, flags); dest.writeInt(mDeviceId); @@ -200,28 +263,40 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeStrongBinder(mTaskFragmentToken); dest.writeStrongBinder(mInitialCallerInfoAccessToken); dest.writeTypedObject(mActivityWindowInfo, flags); + + dest.writeTypedObject(mIntent, flags); + dest.writeTypedObject(mInfo, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private LaunchActivityItem(@NonNull Parcel in) { - setValues(this, in.readStrongBinder(), in.readTypedObject(Intent.CREATOR), in.readInt(), - in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR), - in.readTypedObject(Configuration.CREATOR), in.readInt(), in.readString(), - IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(), - in.readBundle(getClass().getClassLoader()), - in.readPersistableBundle(getClass().getClassLoader()), - in.createTypedArrayList(ResultInfo.CREATOR), - in.createTypedArrayList(ReferrerIntent.CREATOR), - in.readTypedObject(SceneTransitionInfo.CREATOR), - in.readBoolean(), - in.readTypedObject(ProfilerInfo.CREATOR), - in.readStrongBinder(), - IActivityClientController.Stub.asInterface(in.readStrongBinder()), - in.readStrongBinder(), - in.readBoolean(), - in.readStrongBinder(), - in.readStrongBinder(), - in.readTypedObject(ActivityWindowInfo.CREATOR)); + this(in.readStrongBinder() /* activityToken */, + in.readInt() /* ident */, + requireNonNull(in.readTypedObject(Configuration.CREATOR)) /* curConfig */, + requireNonNull(in.readTypedObject(Configuration.CREATOR)) /* overrideConfig */, + in.readInt() /* deviceId */, + in.readString() /* referrer */, + IVoiceInteractor.Stub.asInterface(in.readStrongBinder()) /* voiceInteractor */, + in.readInt() /* procState */, + in.readBundle(in.getClass().getClassLoader()) /* state */, + in.readPersistableBundle(in.getClass().getClassLoader()) /* persistentState */, + in.createTypedArrayList(ResultInfo.CREATOR) /* pendingResults */, + in.createTypedArrayList(ReferrerIntent.CREATOR) /* pendingNewIntents */, + in.readTypedObject(SceneTransitionInfo.CREATOR) /* sceneTransitionInfo */, + in.readBoolean() /* isForward */, + in.readTypedObject(ProfilerInfo.CREATOR) /* profilerInfo */, + in.readStrongBinder() /* assistToken */, + IActivityClientController.Stub.asInterface( + in.readStrongBinder()) /* activityClientController */, + in.readStrongBinder() /* shareableActivityToken */, + in.readBoolean() /* launchedFromBubble */, + in.readStrongBinder() /* taskFragmentToken */, + in.readStrongBinder() /* initialCallerInfoAccessToken */, + requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR)) + /* activityWindowInfo */ + ); + mIntent = in.readTypedObject(Intent.CREATOR); + mInfo = in.readTypedObject(ActivityInfo.CREATOR); } public static final @NonNull Creator<LaunchActivityItem> CREATOR = new Creator<>() { @@ -339,45 +414,4 @@ public class LaunchActivityItem extends ClientTransactionItem { + ",activityWindowInfo=" + mActivityWindowInfo + "}"; } - - // Using the same method to set and clear values to make sure we don't forget anything - private static void setValues(@Nullable LaunchActivityItem instance, - @Nullable IBinder activityToken, @Nullable Intent intent, int ident, - @Nullable ActivityInfo info, @Nullable Configuration curConfig, - @Nullable Configuration overrideConfig, int deviceId, - @Nullable String referrer, @Nullable IVoiceInteractor voiceInteractor, - int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState, - @Nullable List<ResultInfo> pendingResults, - @Nullable List<ReferrerIntent> pendingNewIntents, - @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward, - @Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken, - @Nullable IActivityClientController activityClientController, - @Nullable IBinder shareableActivityToken, boolean launchedFromBubble, - @Nullable IBinder taskFragmentToken, @Nullable IBinder initialCallerInfoAccessToken, - @Nullable ActivityWindowInfo activityWindowInfo) { - instance.mActivityToken = activityToken; - instance.mIntent = intent; - instance.mIdent = ident; - instance.mInfo = info; - instance.mCurConfig = curConfig; - instance.mOverrideConfig = overrideConfig; - instance.mDeviceId = deviceId; - instance.mReferrer = referrer; - instance.mVoiceInteractor = voiceInteractor; - instance.mProcState = procState; - instance.mState = state; - instance.mPersistentState = persistentState; - instance.mPendingResults = pendingResults; - instance.mPendingNewIntents = pendingNewIntents; - instance.mSceneTransitionInfo = sceneTransitionInfo; - instance.mIsForward = isForward; - instance.mProfilerInfo = profilerInfo; - instance.mAssistToken = assistToken; - instance.mActivityClientController = activityClientController; - instance.mShareableActivityToken = shareableActivityToken; - instance.mLaunchedFromBubble = launchedFromBubble; - instance.mTaskFragmentToken = taskFragmentToken; - instance.mInitialCallerInfoAccessToken = initialCallerInfoAccessToken; - instance.mActivityWindowInfo = activityWindowInfo; - } } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index 8706edd26406..1aa563aa6363 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -18,6 +18,8 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; @@ -33,13 +35,26 @@ import java.util.Objects; /** * Activity move to a different display message. + * * @hide */ public class MoveToDisplayItem extends ActivityTransactionItem { - private int mTargetDisplayId; - private Configuration mConfiguration; - private ActivityWindowInfo mActivityWindowInfo; + private final int mTargetDisplayId; + + @NonNull + private final Configuration mConfiguration; + + @NonNull + private final ActivityWindowInfo mActivityWindowInfo; + + public MoveToDisplayItem(@NonNull IBinder activityToken, int targetDisplayId, + @NonNull Configuration configuration, @NonNull ActivityWindowInfo activityWindowInfo) { + super(activityToken); + mTargetDisplayId = targetDisplayId; + mConfiguration = new Configuration(configuration); + mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); + } @Override public void preExecute(@NonNull ClientTransactionHandler client) { @@ -58,38 +73,9 @@ public class MoveToDisplayItem extends ActivityTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } - // ObjectPoolItem implementation - - private MoveToDisplayItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static MoveToDisplayItem obtain(@NonNull IBinder activityToken, int targetDisplayId, - @NonNull Configuration configuration, @NonNull ActivityWindowInfo activityWindowInfo) { - MoveToDisplayItem instance = ObjectPool.obtain(MoveToDisplayItem.class); - if (instance == null) { - instance = new MoveToDisplayItem(); - } - instance.setActivityToken(activityToken); - instance.mTargetDisplayId = targetDisplayId; - instance.mConfiguration = new Configuration(configuration); - instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo); - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mTargetDisplayId = 0; - mConfiguration = null; - mActivityWindowInfo = null; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -98,12 +84,12 @@ public class MoveToDisplayItem extends ActivityTransactionItem { dest.writeTypedObject(mActivityWindowInfo, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private MoveToDisplayItem(@NonNull Parcel in) { super(in); mTargetDisplayId = in.readInt(); - mConfiguration = in.readTypedObject(Configuration.CREATOR); - mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR); + mConfiguration = requireNonNull(in.readTypedObject(Configuration.CREATOR)); + mActivityWindowInfo = requireNonNull(in.readTypedObject(ActivityWindowInfo.CREATOR)); } public static final @NonNull Creator<MoveToDisplayItem> CREATOR = new Creator<>() { diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index acf2ea429e82..b5e9d66ad869 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -38,13 +38,24 @@ import java.util.Objects; /** * New intent message. + * * @hide */ public class NewIntentItem extends ActivityTransactionItem { + // TODO(b/170729553): Mark this with @NonNull and final once @UnsupportedAppUsage removed. + // We cannot do it now to avoid app compatibility regression. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private List<ReferrerIntent> mIntents; - private boolean mResume; + + private final boolean mResume; + + public NewIntentItem(@NonNull IBinder activityToken, + @NonNull List<ReferrerIntent> intents, boolean resume) { + super(activityToken); + mIntents = new ArrayList<>(intents); + mResume = resume; + } @Override public int getPostExecutionState() { @@ -59,36 +70,9 @@ public class NewIntentItem extends ActivityTransactionItem { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } - // ObjectPoolItem implementation - - private NewIntentItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static NewIntentItem obtain(@NonNull IBinder activityToken, - @NonNull List<ReferrerIntent> intents, boolean resume) { - NewIntentItem instance = ObjectPool.obtain(NewIntentItem.class); - if (instance == null) { - instance = new NewIntentItem(); - } - instance.setActivityToken(activityToken); - instance.mIntents = new ArrayList<>(intents); - instance.mResume = resume; - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mIntents = null; - mResume = false; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -96,10 +80,11 @@ public class NewIntentItem extends ActivityTransactionItem { dest.writeTypedList(mIntents, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private NewIntentItem(@NonNull Parcel in) { super(in); mResume = in.readBoolean(); + // TODO(b/170729553): Wrap with requireNonNull once @UnsupportedAppUsage removed. mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR); } diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java deleted file mode 100644 index e86ca37b99ca..000000000000 --- a/core/java/android/app/servertransaction/ObjectPool.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.servertransaction; - -/** - * An object pool that can provide reused objects if available. - * - * @hide - * @deprecated This class is deprecated. Directly create new instances of objects instead of - * obtaining them from this pool. - * TODO(b/311089192): Clean up usages of the pool. - */ -@Deprecated -class ObjectPool { - - /** - * Obtain an instance of a specific class from the pool - * - * @param ignoredItemClass The class of the object we're looking for. - * @return An instance or null if there is none. - * @deprecated This method is deprecated. Directly create new instances of objects instead of - * obtaining them from this pool. - */ - @Deprecated - public static <T extends ObjectPoolItem> T obtain(Class<T> ignoredItemClass) { - return null; - } - - /** - * Recycle the object to the pool. The object should be properly cleared before this. - * - * @param ignoredItem The object to recycle. - * @see ObjectPoolItem#recycle() - * @deprecated This method is deprecated. The object pool is no longer used, so there's - * no need to recycle objects. - */ - @Deprecated - public static <T extends ObjectPoolItem> void recycle(T ignoredItem) { - } -} diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java deleted file mode 100644 index 0141f6eff53b..000000000000 --- a/core/java/android/app/servertransaction/ObjectPoolItem.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.servertransaction; - -/** - * Base interface for all lifecycle items that can be put in object pool. - * - * @hide - * @deprecated This interface is deprecated. Objects should no longer be pooled. - * TODO(b/311089192): Clean up usages of this interface. - */ -@Deprecated -public interface ObjectPoolItem { - /** - * Clear the contents of the item and putting it to a pool. The implementation should call - * {@link ObjectPool#recycle(ObjectPoolItem)} passing itself. - * - * @deprecated This method is deprecated. The object pool is no longer used, so there's - * no need to recycle objects. - */ - @Deprecated - void recycle(); -} diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index d230284287b6..09fc51bf8e5e 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -29,16 +29,29 @@ import android.os.Trace; /** * Request to move an activity to paused state. + * * @hide */ public class PauseActivityItem extends ActivityLifecycleItem { - private static final String TAG = "PauseActivityItem"; + private final boolean mFinished; + private final boolean mUserLeaving; + private final boolean mDontReport; + private final boolean mAutoEnteringPip; - private boolean mFinished; - private boolean mUserLeaving; - private boolean mDontReport; - private boolean mAutoEnteringPip; + public PauseActivityItem(@NonNull IBinder activityToken) { + this(activityToken, false /* finished */, false /* userLeaving */, + true /* dontReport */, false /* autoEnteringPip*/); + } + + public PauseActivityItem(@NonNull IBinder activityToken, boolean finished, + boolean userLeaving, boolean dontReport, boolean autoEnteringPip) { + super(activityToken); + mFinished = finished; + mUserLeaving = userLeaving; + mDontReport = dontReport; + mAutoEnteringPip = autoEnteringPip; + } @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @@ -64,47 +77,9 @@ public class PauseActivityItem extends ActivityLifecycleItem { ActivityClient.getInstance().activityPaused(getActivityToken()); } - // ObjectPoolItem implementation - - private PauseActivityItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static PauseActivityItem obtain(@NonNull IBinder activityToken, boolean finished, - boolean userLeaving, boolean dontReport, boolean autoEnteringPip) { - PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class); - if (instance == null) { - instance = new PauseActivityItem(); - } - instance.setActivityToken(activityToken); - instance.mFinished = finished; - instance.mUserLeaving = userLeaving; - instance.mDontReport = dontReport; - instance.mAutoEnteringPip = autoEnteringPip; - - return instance; - } - - /** Obtain an instance initialized with default params. */ - @NonNull - public static PauseActivityItem obtain(@NonNull IBinder activityToken) { - return obtain(activityToken, false /* finished */, false /* userLeaving */, - true /* dontReport */, false /* autoEnteringPip*/); - } - - @Override - public void recycle() { - super.recycle(); - mFinished = false; - mUserLeaving = false; - mDontReport = false; - mAutoEnteringPip = false; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -114,7 +89,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { dest.writeBoolean(mAutoEnteringPip); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private PauseActivityItem(@NonNull Parcel in) { super(in); mFinished = in.readBoolean(); diff --git a/core/java/android/app/servertransaction/PipStateTransactionItem.java b/core/java/android/app/servertransaction/PipStateTransactionItem.java index 30289ef9f794..ddeb2c1cc0a9 100644 --- a/core/java/android/app/servertransaction/PipStateTransactionItem.java +++ b/core/java/android/app/servertransaction/PipStateTransactionItem.java @@ -28,53 +28,36 @@ import java.util.Objects; /** * Request an activity to enter picture-in-picture mode. + * * @hide */ public final class PipStateTransactionItem extends ActivityTransactionItem { - private PictureInPictureUiState mPipState; - - @Override - public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, - @NonNull PendingTransactionActions pendingActions) { - client.handlePictureInPictureStateChanged(r, mPipState); - } - - // ObjectPoolItem implementation - - private PipStateTransactionItem() {} - - /** Obtain an instance initialized with provided params. */ @NonNull - public static PipStateTransactionItem obtain(@NonNull IBinder activityToken, - @NonNull PictureInPictureUiState pipState) { - PipStateTransactionItem instance = ObjectPool.obtain(PipStateTransactionItem.class); - if (instance == null) { - instance = new PipStateTransactionItem(); - } - instance.setActivityToken(activityToken); - instance.mPipState = pipState; + private final PictureInPictureUiState mPipState; - return instance; + public PipStateTransactionItem(@NonNull IBinder activityToken, + @NonNull PictureInPictureUiState pipState) { + super(activityToken); + mPipState = pipState; } @Override - public void recycle() { - super.recycle(); - mPipState = null; - ObjectPool.recycle(this); + public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, + @NonNull PendingTransactionActions pendingActions) { + client.handlePictureInPictureStateChanged(r, mPipState); } // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); mPipState.writeToParcel(dest, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private PipStateTransactionItem(@NonNull Parcel in) { super(in); mPipState = PictureInPictureUiState.CREATOR.createFromParcel(in); diff --git a/core/java/android/app/servertransaction/RefreshCallbackItem.java b/core/java/android/app/servertransaction/RefreshCallbackItem.java index a3f82e907215..57fd97af235a 100644 --- a/core/java/android/app/servertransaction/RefreshCallbackItem.java +++ b/core/java/android/app/servertransaction/RefreshCallbackItem.java @@ -44,7 +44,20 @@ public class RefreshCallbackItem extends ActivityTransactionItem { // Whether refresh should happen using the "stopped -> resumed" cycle or // "paused -> resumed" cycle. @LifecycleState - private int mPostExecutionState; + private final int mPostExecutionState; + + /** + * Creates a new RefreshCallbackItem. + * + * @param activityToken the target client activity. + * @param postExecutionState indicating whether refresh should happen using the + * "stopped -> "resumed" cycle or "paused -> resumed" cycle. + */ + public RefreshCallbackItem( + @NonNull IBinder activityToken, @LifecycleState int postExecutionState) { + super(activityToken); + mPostExecutionState = postExecutionState; + } @Override public void execute(@NonNull ClientTransactionHandler client, @@ -67,47 +80,21 @@ public class RefreshCallbackItem extends ActivityTransactionItem { return false; } - // ObjectPoolItem implementation - - @Override - public void recycle() { - super.recycle(); - ObjectPool.recycle(this); - } - - /** - * Obtain an instance initialized with provided params. - * @param postExecutionState indicating whether refresh should happen using the - * "stopped -> resumed" cycle or "paused -> resumed" cycle. - */ - @NonNull - public static RefreshCallbackItem obtain(@NonNull IBinder activityToken, - @LifecycleState int postExecutionState) { - if (postExecutionState != ON_STOP && postExecutionState != ON_PAUSE) { - throw new IllegalArgumentException( - "Only ON_STOP or ON_PAUSE are allowed as a post execution state for " - + "RefreshCallbackItem but got " + postExecutionState); - } - RefreshCallbackItem instance = - ObjectPool.obtain(RefreshCallbackItem.class); - if (instance == null) { - instance = new RefreshCallbackItem(); - } - instance.setActivityToken(activityToken); - instance.mPostExecutionState = postExecutionState; - return instance; - } - - private RefreshCallbackItem() {} - // Parcelable implementation + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mPostExecutionState); } + /** Reads from Parcel. */ + private RefreshCallbackItem(@NonNull Parcel in) { + super(in); + mPostExecutionState = in.readInt(); + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { @@ -134,11 +121,6 @@ public class RefreshCallbackItem extends ActivityTransactionItem { + ",mPostExecutionState=" + mPostExecutionState + "}"; } - private RefreshCallbackItem(@NonNull Parcel in) { - super(in); - mPostExecutionState = in.readInt(); - } - public static final @NonNull Creator<RefreshCallbackItem> CREATOR = new Creator<>() { public RefreshCallbackItem createFromParcel(@NonNull Parcel in) { diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index 4a0ea98ccb89..a28791f6684e 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityClient; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessState; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; @@ -30,22 +31,37 @@ import android.os.Trace; /** * Request to move an activity to resumed state. + * * @hide */ public class ResumeActivityItem extends ActivityLifecycleItem { - private static final String TAG = "ResumeActivityItem"; + @ProcessState + private final int mProcState; + + private final boolean mIsForward; - private int mProcState; - private boolean mUpdateProcState; - private boolean mIsForward; // Whether we should send compat fake focus when the activity is resumed. This is needed // because some game engines wait to get focus before drawing the content of the app. - private boolean mShouldSendCompatFakeFocus; + private final boolean mShouldSendCompatFakeFocus; + + public ResumeActivityItem(@NonNull IBinder activityToken, boolean isForward, + boolean shouldSendCompatFakeFocus) { + this(activityToken, ActivityManager.PROCESS_STATE_UNKNOWN, isForward, + shouldSendCompatFakeFocus); + } + + public ResumeActivityItem(@NonNull IBinder activityToken, @ProcessState int procState, + boolean isForward, boolean shouldSendCompatFakeFocus) { + super(activityToken); + mProcState = procState; + mIsForward = isForward; + mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus; + } @Override public void preExecute(@NonNull ClientTransactionHandler client) { - if (mUpdateProcState) { + if (mProcState != ActivityManager.PROCESS_STATE_UNKNOWN) { client.updateProcessState(mProcState, false); } } @@ -72,71 +88,21 @@ public class ResumeActivityItem extends ActivityLifecycleItem { return ON_RESUME; } - // ObjectPoolItem implementation - - private ResumeActivityItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static ResumeActivityItem obtain(@NonNull IBinder activityToken, int procState, - boolean isForward, boolean shouldSendCompatFakeFocus) { - ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); - if (instance == null) { - instance = new ResumeActivityItem(); - } - instance.setActivityToken(activityToken); - instance.mProcState = procState; - instance.mUpdateProcState = true; - instance.mIsForward = isForward; - instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus; - - return instance; - } - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static ResumeActivityItem obtain(@NonNull IBinder activityToken, boolean isForward, - boolean shouldSendCompatFakeFocus) { - ResumeActivityItem instance = ObjectPool.obtain(ResumeActivityItem.class); - if (instance == null) { - instance = new ResumeActivityItem(); - } - instance.setActivityToken(activityToken); - instance.mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; - instance.mUpdateProcState = false; - instance.mIsForward = isForward; - instance.mShouldSendCompatFakeFocus = shouldSendCompatFakeFocus; - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; - mUpdateProcState = false; - mIsForward = false; - mShouldSendCompatFakeFocus = false; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mProcState); - dest.writeBoolean(mUpdateProcState); dest.writeBoolean(mIsForward); dest.writeBoolean(mShouldSendCompatFakeFocus); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private ResumeActivityItem(@NonNull Parcel in) { super(in); mProcState = in.readInt(); - mUpdateProcState = in.readBoolean(); mIsForward = in.readBoolean(); mShouldSendCompatFakeFocus = in.readBoolean(); } @@ -160,7 +126,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { return false; } final ResumeActivityItem other = (ResumeActivityItem) o; - return mProcState == other.mProcState && mUpdateProcState == other.mUpdateProcState + return mProcState == other.mProcState && mIsForward == other.mIsForward && mShouldSendCompatFakeFocus == other.mShouldSendCompatFakeFocus; } @@ -170,7 +136,6 @@ public class ResumeActivityItem extends ActivityLifecycleItem { int result = 17; result = 31 * result + super.hashCode(); result = 31 * result + mProcState; - result = 31 * result + (mUpdateProcState ? 1 : 0); result = 31 * result + (mIsForward ? 1 : 0); result = 31 * result + (mShouldSendCompatFakeFocus ? 1 : 0); return result; @@ -180,7 +145,6 @@ public class ResumeActivityItem extends ActivityLifecycleItem { public String toString() { return "ResumeActivityItem{" + super.toString() + ",procState=" + mProcState - + ",updateProcState=" + mUpdateProcState + ",isForward=" + mIsForward + ",shouldSendCompatFakeFocus=" + mShouldSendCompatFakeFocus + "}"; } diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java index a0f93ce00ec8..6bae92b1e0a4 100644 --- a/core/java/android/app/servertransaction/StartActivityItem.java +++ b/core/java/android/app/servertransaction/StartActivityItem.java @@ -29,13 +29,19 @@ import android.os.Trace; /** * Request to move an activity to started and visible state. + * * @hide */ public class StartActivityItem extends ActivityLifecycleItem { - private static final String TAG = "StartActivityItem"; + @Nullable + private final SceneTransitionInfo mSceneTransitionInfo; - private SceneTransitionInfo mSceneTransitionInfo; + public StartActivityItem(@NonNull IBinder activityToken, + @Nullable SceneTransitionInfo sceneTransitionInfo) { + super(activityToken); + mSceneTransitionInfo = sceneTransitionInfo; + } @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @@ -50,41 +56,16 @@ public class StartActivityItem extends ActivityLifecycleItem { return ON_START; } - // ObjectPoolItem implementation - - private StartActivityItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static StartActivityItem obtain(@NonNull IBinder activityToken, - @Nullable SceneTransitionInfo sceneTransitionInfo) { - StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class); - if (instance == null) { - instance = new StartActivityItem(); - } - instance.setActivityToken(activityToken); - instance.mSceneTransitionInfo = sceneTransitionInfo; - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mSceneTransitionInfo = null; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeTypedObject(mSceneTransitionInfo, flags); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private StartActivityItem(@NonNull Parcel in) { super(in); mSceneTransitionInfo = in.readTypedObject(SceneTransitionInfo.CREATOR); diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index def7b3fd9987..012b82e8b1eb 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -27,11 +27,14 @@ import android.os.Trace; /** * Request to move an activity to stopped state. + * * @hide */ public class StopActivityItem extends ActivityLifecycleItem { - private static final String TAG = "StopActivityItem"; + public StopActivityItem(@NonNull IBinder activityToken) { + super(activityToken); + } @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @@ -53,34 +56,9 @@ public class StopActivityItem extends ActivityLifecycleItem { return ON_STOP; } - // ObjectPoolItem implementation - - private StopActivityItem() {} - - /** - * Obtain an instance initialized with provided params. - * @param activityToken the activity that stops. - */ - @NonNull - public static StopActivityItem obtain(@NonNull IBinder activityToken) { - StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); - if (instance == null) { - instance = new StopActivityItem(); - } - instance.setActivityToken(activityToken); - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Read from Parcel. */ + /** Reads from Parcel. */ private StopActivityItem(@NonNull Parcel in) { super(in); } diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java index 23d4505c1c9e..b5f83459cc5e 100644 --- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java +++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; @@ -28,11 +29,17 @@ import android.os.Trace; /** * Top resumed activity changed callback. + * * @hide */ public class TopResumedActivityChangeItem extends ActivityTransactionItem { - private boolean mOnTop; + private final boolean mOnTop; + + public TopResumedActivityChangeItem(@NonNull IBinder activityToken, boolean onTop) { + super(activityToken); + mOnTop = onTop; + } @Override public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r, @@ -58,42 +65,16 @@ public class TopResumedActivityChangeItem extends ActivityTransactionItem { ActivityClient.getInstance().activityTopResumedStateLost(); } - // ObjectPoolItem implementation - - private TopResumedActivityChangeItem() {} - - /** Obtain an instance initialized with provided params. */ - @NonNull - public static TopResumedActivityChangeItem obtain(@NonNull IBinder activityToken, - boolean onTop) { - TopResumedActivityChangeItem instance = - ObjectPool.obtain(TopResumedActivityChangeItem.class); - if (instance == null) { - instance = new TopResumedActivityChangeItem(); - } - instance.setActivityToken(activityToken); - instance.mOnTop = onTop; - - return instance; - } - - @Override - public void recycle() { - super.recycle(); - mOnTop = false; - ObjectPool.recycle(this); - } - // Parcelable implementation - /** Write to Parcel. */ + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeBoolean(mOnTop); } - /** Read from Parcel. */ + /** Reads from Parcel. */ private TopResumedActivityChangeItem(@NonNull Parcel in) { super(in); mOnTop = in.readBoolean(); @@ -131,7 +112,6 @@ public class TopResumedActivityChangeItem extends ActivityTransactionItem { @Override public String toString() { - return "TopResumedActivityChangeItem{" + super.toString() - + ",onTop=" + mOnTop + "}"; + return "TopResumedActivityChangeItem{" + super.toString() + ",onTop=" + mOnTop + "}"; } } diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java index 68012e235f97..3a23e6b1b0a6 100644 --- a/core/java/android/app/servertransaction/TransactionExecutor.java +++ b/core/java/android/app/servertransaction/TransactionExecutor.java @@ -45,6 +45,7 @@ import java.util.List; /** * Class that manages transaction execution in the correct order. + * * @hide */ public class TransactionExecutor { diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 9f622e9e7dda..785fa59a2c13 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -42,6 +42,7 @@ import java.util.List; /** * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution. + * * @hide */ public class TransactionExecutorHelper { @@ -54,7 +55,7 @@ public class TransactionExecutorHelper { // Temp holder for lifecycle path. // No direct transition between two states should take more than one complete cycle of 6 states. @ActivityLifecycleItem.LifecycleState - private IntArray mLifecycleSequence = new IntArray(6); + private final IntArray mLifecycleSequence = new IntArray(6 /* initialCapacity */); /** * Calculate the path through main lifecycle states for an activity and fill @@ -197,13 +198,13 @@ public class TransactionExecutorHelper { // Fall through to return the PAUSE item to ensure the activity is properly // resumed while relaunching. case ON_PAUSE: - lifecycleItem = PauseActivityItem.obtain(r.token); + lifecycleItem = new PauseActivityItem(r.token); break; case ON_STOP: - lifecycleItem = StopActivityItem.obtain(r.token); + lifecycleItem = new StopActivityItem(r.token); break; default: - lifecycleItem = ResumeActivityItem.obtain(r.token, false /* isForward */, + lifecycleItem = new ResumeActivityItem(r.token, false /* isForward */, false /* shouldSendCompatFakeFocus */); break; } diff --git a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java index 11947e9b11d8..5068c39c99a2 100644 --- a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java +++ b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java @@ -29,12 +29,24 @@ import java.util.Objects; /** * Transfer a splash screen view to an Activity. + * * @hide */ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem { - private SplashScreenViewParcelable mSplashScreenViewParcelable; - private SurfaceControl mStartingWindowLeash; + @Nullable + private final SplashScreenViewParcelable mSplashScreenViewParcelable; + + @Nullable + private final SurfaceControl mStartingWindowLeash; + + public TransferSplashScreenViewStateItem(@NonNull IBinder activityToken, + @Nullable SplashScreenViewParcelable parcelable, + @Nullable SurfaceControl startingWindowLeash) { + super(activityToken); + mSplashScreenViewParcelable = parcelable; + mStartingWindowLeash = startingWindowLeash; + } @Override public void execute(@NonNull ClientTransactionHandler client, @@ -43,14 +55,9 @@ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem { client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash); } - @Override - public void recycle() { - super.recycle(); - mSplashScreenViewParcelable = null; - mStartingWindowLeash = null; - ObjectPool.recycle(this); - } + // Parcelable implementation + /** Writes to Parcel. */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -58,31 +65,13 @@ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem { dest.writeTypedObject(mStartingWindowLeash, flags); } - private TransferSplashScreenViewStateItem() {} - + /** Reads from Parcel. */ private TransferSplashScreenViewStateItem(@NonNull Parcel in) { super(in); mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR); mStartingWindowLeash = in.readTypedObject(SurfaceControl.CREATOR); } - /** Obtain an instance initialized with provided params. */ - @NonNull - public static TransferSplashScreenViewStateItem obtain( - @NonNull IBinder activityToken, @Nullable SplashScreenViewParcelable parcelable, - @Nullable SurfaceControl startingWindowLeash) { - TransferSplashScreenViewStateItem instance = - ObjectPool.obtain(TransferSplashScreenViewStateItem.class); - if (instance == null) { - instance = new TransferSplashScreenViewStateItem(); - } - instance.setActivityToken(activityToken); - instance.mSplashScreenViewParcelable = parcelable; - instance.mStartingWindowLeash = startingWindowLeash; - - return instance; - } - public static final @NonNull Creator<TransferSplashScreenViewStateItem> CREATOR = new Creator<>() { public TransferSplashScreenViewStateItem createFromParcel(@NonNull Parcel in) { diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java index f6a72915e639..b2e87bd3e9e2 100644 --- a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java +++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java @@ -30,44 +30,27 @@ import java.util.Objects; /** * {@link android.window.WindowContext} configuration change message. + * * @hide */ public class WindowContextInfoChangeItem extends ClientTransactionItem { - @Nullable - private IBinder mClientToken; - @Nullable - private WindowContextInfo mInfo; - - @Override - public void execute(@NonNull ClientTransactionHandler client, - @NonNull PendingTransactionActions pendingActions) { - client.handleWindowContextInfoChanged(mClientToken, mInfo); - } + @NonNull + private final IBinder mClientToken; - // ObjectPoolItem implementation + @NonNull + private final WindowContextInfo mInfo; - private WindowContextInfoChangeItem() {} - - /** Obtains an instance initialized with provided params. */ - public static WindowContextInfoChangeItem obtain( + public WindowContextInfoChangeItem( @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) { - WindowContextInfoChangeItem instance = - ObjectPool.obtain(WindowContextInfoChangeItem.class); - if (instance == null) { - instance = new WindowContextInfoChangeItem(); - } - instance.mClientToken = requireNonNull(clientToken); - instance.mInfo = new WindowContextInfo(new Configuration(config), displayId); - - return instance; + mClientToken = requireNonNull(clientToken); + mInfo = new WindowContextInfo(new Configuration(config), displayId); } @Override - public void recycle() { - mClientToken = null; - mInfo = null; - ObjectPool.recycle(this); + public void execute(@NonNull ClientTransactionHandler client, + @NonNull PendingTransactionActions pendingActions) { + client.handleWindowContextInfoChanged(mClientToken, mInfo); } // Parcelable implementation @@ -82,7 +65,7 @@ public class WindowContextInfoChangeItem extends ClientTransactionItem { /** Reads from Parcel. */ private WindowContextInfoChangeItem(@NonNull Parcel in) { mClientToken = in.readStrongBinder(); - mInfo = in.readTypedObject(WindowContextInfo.CREATOR); + mInfo = requireNonNull(in.readTypedObject(WindowContextInfo.CREATOR)); } public static final @NonNull Creator<WindowContextInfoChangeItem> CREATOR = diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java index 1bea4682928a..76b39d5ba1d9 100644 --- a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java +++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java @@ -28,12 +28,17 @@ import java.util.Objects; /** * {@link android.window.WindowContext} window removal message. + * * @hide */ public class WindowContextWindowRemovalItem extends ClientTransactionItem { - @Nullable - private IBinder mClientToken; + @NonNull + private final IBinder mClientToken; + + public WindowContextWindowRemovalItem(@NonNull IBinder clientToken) { + mClientToken = requireNonNull(clientToken); + } @Override public void execute(@NonNull ClientTransactionHandler client, @@ -41,28 +46,6 @@ public class WindowContextWindowRemovalItem extends ClientTransactionItem { client.handleWindowContextWindowRemoval(mClientToken); } - // ObjectPoolItem implementation - - private WindowContextWindowRemovalItem() {} - - /** Obtains an instance initialized with provided params. */ - public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) { - WindowContextWindowRemovalItem instance = - ObjectPool.obtain(WindowContextWindowRemovalItem.class); - if (instance == null) { - instance = new WindowContextWindowRemovalItem(); - } - instance.mClientToken = requireNonNull(clientToken); - - return instance; - } - - @Override - public void recycle() { - mClientToken = null; - ObjectPool.recycle(this); - } - // Parcelable implementation /** Writes to Parcel. */ diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 611393274285..e8d7e1efe315 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -600,7 +600,7 @@ public class SensorEvent { * * <p> * This sensor must be able to detect and report an on-body to off-body - * transition within 1 second of the device being removed from the body, + * transition within 3 seconds of the device being removed from the body, * and must be able to detect and report an off-body to on-body transition * within 5 seconds of the device being put back onto the body. * </p> diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 0cd280002dbf..b4ad050c8b5d 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -75,3 +75,13 @@ flag { description: "Enables a developer overlay that displays raw touchpad input data and gesture recognition status in real-time." bug: "286551975" } + +flag { + namespace: "input_native" + name: "keyboard_layout_manager_multi_user_ime_setup" + description: "Update KeyboardLayoutManager to work correctly with multi-user IME setup" + bug: "354333072" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java index f32a1f831a07..77d6cb762e06 100644 --- a/core/java/android/os/CombinedVibration.java +++ b/core/java/android/os/CombinedVibration.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.TestApi; +import android.os.vibrator.Flags; import android.util.SparseArray; import com.android.internal.util.Preconditions; @@ -152,6 +153,9 @@ public abstract class CombinedVibration implements Parcelable { /** @hide */ public abstract boolean hasVibrator(int vibratorId); + /** @hide */ + public abstract boolean hasVendorEffects(); + /** * Returns a compact version of the {@link #toString()} result for debugging purposes. * @@ -424,6 +428,15 @@ public abstract class CombinedVibration implements Parcelable { return true; } + /** @hide */ + @Override + public boolean hasVendorEffects() { + if (!Flags.vendorVibrationEffects()) { + return false; + } + return mEffect instanceof VibrationEffect.VendorEffect; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -605,6 +618,20 @@ public abstract class CombinedVibration implements Parcelable { return mEffects.indexOfKey(vibratorId) >= 0; } + /** @hide */ + @Override + public boolean hasVendorEffects() { + if (!Flags.vendorVibrationEffects()) { + return false; + } + for (int i = 0; i < mEffects.size(); i++) { + if (mEffects.get(i) instanceof VibrationEffect.VendorEffect) { + return true; + } + } + return false; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -838,6 +865,17 @@ public abstract class CombinedVibration implements Parcelable { return false; } + /** @hide */ + @Override + public boolean hasVendorEffects() { + for (int i = 0; i < mEffects.size(); i++) { + if (mEffects.get(i).hasVendorEffects()) { + return true; + } + } + return false; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index efbd96bc35cb..44edf298beeb 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -16,18 +16,25 @@ package android.os; +import static android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; +import android.hardware.vibrator.IVibrator; import android.hardware.vibrator.V1_0.EffectStrength; import android.hardware.vibrator.V1_3.Effect; import android.net.Uri; +import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; @@ -46,6 +53,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.StringJoiner; +import java.util.function.BiFunction; /** * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}. @@ -53,6 +61,9 @@ import java.util.StringJoiner; * <p>These effects may be any number of things, from single shot vibrations to complex waveforms. */ public abstract class VibrationEffect implements Parcelable { + private static final int PARCEL_TOKEN_COMPOSED = 1; + private static final int PARCEL_TOKEN_VENDOR_EFFECT = 2; + // Stevens' coefficient to scale the perceived vibration intensity. private static final float SCALE_GAMMA = 0.65f; // If a vibration is playing for longer than 1s, it's probably not haptic feedback @@ -316,6 +327,28 @@ public abstract class VibrationEffect implements Parcelable { } /** + * Create a vendor-defined vibration effect. + * + * <p>Vendor effects offer more flexibility for accessing vendor-specific vibrator capabilities, + * enabling control over any vibration parameter and more generic vibration waveforms for apps + * provided by the device vendor. + * + * <p>This requires hardware-specific implementation of the effect and will not have any + * platform fallback support. + * + * @param effect An opaque representation of the vibration effect which can also be serialized. + * @return The desired effect. + * @hide + */ + @NonNull + @SystemApi + @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS) + @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS) + public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) { + return new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH, VendorEffect.DEFAULT_SCALE); + } + + /** * Get a predefined vibration effect. * * <p>Predefined effects are a set of common vibration effects that should be identical, @@ -508,7 +541,7 @@ public abstract class VibrationEffect implements Parcelable { * Gets the estimated duration of the vibration in milliseconds. * * <p>For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this - * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where + * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. predefined effects where * the length is device and potentially run-time dependent), this returns -1. * * @hide @@ -550,7 +583,19 @@ public abstract class VibrationEffect implements Parcelable { * * @hide */ - public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude); + @NonNull + public abstract VibrationEffect resolve(int defaultAmplitude); + + /** + * Applies given effect strength to predefined and vendor-specific effects. + * + * @param effectStrength new effect strength to be applied, one of + * VibrationEffect.EFFECT_STRENGTH_*. + * @return this if there is no change, or a copy of this effect with new strength otherwise + * @hide + */ + @NonNull + public abstract VibrationEffect applyEffectStrength(int effectStrength); /** * Scale the vibration effect intensity with the given constraints. @@ -562,7 +607,20 @@ public abstract class VibrationEffect implements Parcelable { * * @hide */ - public abstract <T extends VibrationEffect> T scale(float scaleFactor); + @NonNull + public abstract VibrationEffect scale(float scaleFactor); + + /** + * Performs a linear scaling on the effect intensity with the given factor. + * + * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will + * scale down the intensity, values larger than 1 will scale up + * @return this if there is no scaling to be done, or a copy of this effect with scaled + * vibration intensity otherwise + * @hide + */ + @NonNull + public abstract VibrationEffect scaleLinearly(float scaleFactor); /** * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and @@ -651,38 +709,26 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ public static String effectIdToString(int effectId) { - switch (effectId) { - case EFFECT_CLICK: - return "CLICK"; - case EFFECT_TICK: - return "TICK"; - case EFFECT_HEAVY_CLICK: - return "HEAVY_CLICK"; - case EFFECT_DOUBLE_CLICK: - return "DOUBLE_CLICK"; - case EFFECT_POP: - return "POP"; - case EFFECT_THUD: - return "THUD"; - case EFFECT_TEXTURE_TICK: - return "TEXTURE_TICK"; - default: - return Integer.toString(effectId); - } + return switch (effectId) { + case EFFECT_CLICK -> "CLICK"; + case EFFECT_TICK -> "TICK"; + case EFFECT_HEAVY_CLICK -> "HEAVY_CLICK"; + case EFFECT_DOUBLE_CLICK -> "DOUBLE_CLICK"; + case EFFECT_POP -> "POP"; + case EFFECT_THUD -> "THUD"; + case EFFECT_TEXTURE_TICK -> "TEXTURE_TICK"; + default -> Integer.toString(effectId); + }; } /** @hide */ public static String effectStrengthToString(int effectStrength) { - switch (effectStrength) { - case EFFECT_STRENGTH_LIGHT: - return "LIGHT"; - case EFFECT_STRENGTH_MEDIUM: - return "MEDIUM"; - case EFFECT_STRENGTH_STRONG: - return "STRONG"; - default: - return Integer.toString(effectStrength); - } + return switch (effectStrength) { + case EFFECT_STRENGTH_LIGHT -> "LIGHT"; + case EFFECT_STRENGTH_MEDIUM -> "MEDIUM"; + case EFFECT_STRENGTH_STRONG -> "STRONG"; + default -> Integer.toString(effectStrength); + }; } /** @@ -712,12 +758,15 @@ public abstract class VibrationEffect implements Parcelable { private final ArrayList<VibrationEffectSegment> mSegments; private final int mRepeatIndex; + /** @hide */ Composed(@NonNull Parcel in) { - this(in.readArrayList( - VibrationEffectSegment.class.getClassLoader(), VibrationEffectSegment.class), + this(Objects.requireNonNull(in.readArrayList( + VibrationEffectSegment.class.getClassLoader(), + VibrationEffectSegment.class)), in.readInt()); } + /** @hide */ Composed(@NonNull VibrationEffectSegment segment) { this(Arrays.asList(segment), /* repeatIndex= */ -1); } @@ -844,7 +893,7 @@ public abstract class VibrationEffect implements Parcelable { } int segmentCount = mSegments.size(); if (segmentCount > MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE) { - // Vibration has some prebaked or primitive constants, it should be limited to the + // Vibration has some predefined or primitive constants, it should be limited to the // max composition size used to classify haptic feedbacks. return false; } @@ -867,34 +916,28 @@ public abstract class VibrationEffect implements Parcelable { @NonNull @Override public Composed resolve(int defaultAmplitude) { - int segmentCount = mSegments.size(); - ArrayList<VibrationEffectSegment> resolvedSegments = new ArrayList<>(segmentCount); - for (int i = 0; i < segmentCount; i++) { - resolvedSegments.add(mSegments.get(i).resolve(defaultAmplitude)); - } - if (resolvedSegments.equals(mSegments)) { - return this; - } - Composed resolved = new Composed(resolvedSegments, mRepeatIndex); - resolved.validate(); - return resolved; + return applyToSegments(VibrationEffectSegment::resolve, defaultAmplitude); + } + + /** @hide */ + @NonNull + @Override + public VibrationEffect applyEffectStrength(int effectStrength) { + return applyToSegments(VibrationEffectSegment::applyEffectStrength, effectStrength); } /** @hide */ @NonNull @Override public Composed scale(float scaleFactor) { - int segmentCount = mSegments.size(); - ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount); - for (int i = 0; i < segmentCount; i++) { - scaledSegments.add(mSegments.get(i).scale(scaleFactor)); - } - if (scaledSegments.equals(mSegments)) { - return this; - } - Composed scaled = new Composed(scaledSegments, mRepeatIndex); - scaled.validate(); - return scaled; + return applyToSegments(VibrationEffectSegment::scale, scaleFactor); + } + + /** @hide */ + @NonNull + @Override + public Composed scaleLinearly(float scaleFactor) { + return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor); } /** @hide */ @@ -926,10 +969,9 @@ public abstract class VibrationEffect implements Parcelable { if (this == o) { return true; } - if (!(o instanceof Composed)) { + if (!(o instanceof Composed other)) { return false; } - Composed other = (Composed) o; return mSegments.equals(other.mSegments) && mRepeatIndex == other.mRepeatIndex; } @@ -969,6 +1011,7 @@ public abstract class VibrationEffect implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(PARCEL_TOKEN_COMPOSED); out.writeList(mSegments); out.writeInt(mRepeatIndex); } @@ -1011,6 +1054,208 @@ public abstract class VibrationEffect implements Parcelable { return stepSegment; } + + private <T> Composed applyToSegments( + BiFunction<VibrationEffectSegment, T, VibrationEffectSegment> function, T param) { + int segmentCount = mSegments.size(); + ArrayList<VibrationEffectSegment> updatedSegments = new ArrayList<>(segmentCount); + for (int i = 0; i < segmentCount; i++) { + updatedSegments.add(function.apply(mSegments.get(i), param)); + } + if (mSegments.equals(updatedSegments)) { + return this; + } + Composed updated = new Composed(updatedSegments, mRepeatIndex); + updated.validate(); + return updated; + } + } + + /** + * Implementation of {@link VibrationEffect} described by a generic {@link PersistableBundle} + * defined by vendors. + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS) + public static final class VendorEffect extends VibrationEffect { + /** @hide */ + public static final int DEFAULT_STRENGTH = VibrationEffect.EFFECT_STRENGTH_MEDIUM; + /** @hide */ + public static final float DEFAULT_SCALE = 1.0f; + + private final PersistableBundle mVendorData; + private final int mEffectStrength; + private final float mLinearScale; + + /** @hide */ + VendorEffect(@NonNull Parcel in) { + this(Objects.requireNonNull( + in.readPersistableBundle(VibrationEffect.class.getClassLoader())), + in.readInt(), in.readFloat()); + } + + /** @hide */ + public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength, + float linearScale) { + mVendorData = vendorData; + mEffectStrength = effectStrength; + mLinearScale = linearScale; + } + + @NonNull + public PersistableBundle getVendorData() { + return mVendorData; + } + + public int getEffectStrength() { + return mEffectStrength; + } + + public float getLinearScale() { + return mLinearScale; + } + + /** @hide */ + @Override + @Nullable + public long[] computeCreateWaveformOffOnTimingsOrNull() { + return null; + } + + /** @hide */ + @Override + public void validate() { + Preconditions.checkArgument(!mVendorData.isEmpty(), + "Vendor effect bundle must be non-empty"); + } + + @Override + public long getDuration() { + return -1; // UNKNOWN + } + + /** @hide */ + @Override + public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { + return vibratorInfo.hasCapability(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + } + + /** @hide */ + @Override + public boolean isHapticFeedbackCandidate() { + return false; + } + + /** @hide */ + @NonNull + @Override + public VendorEffect resolve(int defaultAmplitude) { + return this; + } + + /** @hide */ + @NonNull + @Override + public VibrationEffect applyEffectStrength(int effectStrength) { + if (mEffectStrength == effectStrength) { + return this; + } + VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale); + updated.validate(); + return updated; + } + + /** @hide */ + @NonNull + @Override + public VendorEffect scale(float scaleFactor) { + // Vendor effect strength cannot be scaled with this method. + return this; + } + + /** @hide */ + @NonNull + @Override + public VibrationEffect scaleLinearly(float scaleFactor) { + if (Float.compare(mLinearScale, scaleFactor) == 0) { + return this; + } + VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor); + updated.validate(); + return updated; + } + + /** @hide */ + @NonNull + @Override + public VendorEffect applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) { + return this; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VendorEffect other)) { + return false; + } + return mEffectStrength == other.mEffectStrength + && (Float.compare(mLinearScale, other.mLinearScale) == 0) + // Make sure it calls unparcel for both before calling BaseBundle.kindofEquals. + && mVendorData.size() == other.mVendorData.size() + && BaseBundle.kindofEquals(mVendorData, other.mVendorData); + } + + @Override + public int hashCode() { + // PersistableBundle does not implement hashCode, so use its size as a shortcut. + return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale); + } + + @Override + public String toString() { + return String.format(Locale.ROOT, + "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}", + mVendorData, effectStrengthToString(mEffectStrength), mLinearScale); + } + + /** @hide */ + @Override + public String toDebugString() { + return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f", + mVendorData.toShortString(), effectStrengthToString(mEffectStrength), + mLinearScale); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT); + out.writePersistableBundle(mVendorData); + out.writeInt(mEffectStrength); + out.writeFloat(mLinearScale); + } + + @NonNull + public static final Creator<VendorEffect> CREATOR = + new Creator<VendorEffect>() { + @Override + public VendorEffect createFromParcel(Parcel in) { + return new VendorEffect(in); + } + + @Override + public VendorEffect[] newArray(int size) { + return new VendorEffect[size]; + } + }; } /** @@ -1249,7 +1494,9 @@ public abstract class VibrationEffect implements Parcelable { if (mRepeatIndex >= 0) { throw new UnreachableAfterRepeatingIndefinitelyException(); } - Composed composed = (Composed) effect; + if (!(effect instanceof Composed composed)) { + throw new IllegalArgumentException("Can't add vendor effects to composition."); + } if (composed.getRepeatIndex() >= 0) { // Start repeating from the index relative to the composed waveform. mRepeatIndex = mSegments.size() + composed.getRepeatIndex(); @@ -1285,28 +1532,18 @@ public abstract class VibrationEffect implements Parcelable { * @hide */ public static String primitiveToString(@PrimitiveType int id) { - switch (id) { - case PRIMITIVE_NOOP: - return "NOOP"; - case PRIMITIVE_CLICK: - return "CLICK"; - case PRIMITIVE_THUD: - return "THUD"; - case PRIMITIVE_SPIN: - return "SPIN"; - case PRIMITIVE_QUICK_RISE: - return "QUICK_RISE"; - case PRIMITIVE_SLOW_RISE: - return "SLOW_RISE"; - case PRIMITIVE_QUICK_FALL: - return "QUICK_FALL"; - case PRIMITIVE_TICK: - return "TICK"; - case PRIMITIVE_LOW_TICK: - return "LOW_TICK"; - default: - return Integer.toString(id); - } + return switch (id) { + case PRIMITIVE_NOOP -> "NOOP"; + case PRIMITIVE_CLICK -> "CLICK"; + case PRIMITIVE_THUD -> "THUD"; + case PRIMITIVE_SPIN -> "SPIN"; + case PRIMITIVE_QUICK_RISE -> "QUICK_RISE"; + case PRIMITIVE_SLOW_RISE -> "SLOW_RISE"; + case PRIMITIVE_QUICK_FALL -> "QUICK_FALL"; + case PRIMITIVE_TICK -> "TICK"; + case PRIMITIVE_LOW_TICK -> "LOW_TICK"; + default -> Integer.toString(id); + }; } } @@ -1640,7 +1877,17 @@ public abstract class VibrationEffect implements Parcelable { new Parcelable.Creator<VibrationEffect>() { @Override public VibrationEffect createFromParcel(Parcel in) { - return new Composed(in); + switch (in.readInt()) { + case PARCEL_TOKEN_COMPOSED: + return new Composed(in); + case PARCEL_TOKEN_VENDOR_EFFECT: + if (Flags.vendorVibrationEffects()) { + return new VendorEffect(in); + } // else fall through + default: + throw new IllegalStateException( + "Unexpected vibration effect type token in parcel."); + } } @Override public VibrationEffect[] newArray(int size) { diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index ad2f59db46ff..f4e2a7e28d8c 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -53,3 +53,14 @@ flag { purpose: PURPOSE_FEATURE } } + +flag { + namespace: "haptics" + name: "vendor_vibration_effects" + is_exported: true + description: "Enabled System APIs for vendor-defined vibration effects" + bug: "345454923" + metadata { + purpose: PURPOSE_FEATURE + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ff389208a579..850b9795e41a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2630,7 +2630,7 @@ public final class Settings { /** * Activity Action: Show screen that let user select its Autofill Service. * <p> - * Input: Intent's data URI set with an application name, using the + * Input: Intent's data URI set with an application package name, using the * "package" schema (like "package:com.my.app"). * * <p> @@ -2650,7 +2650,7 @@ public final class Settings { /** * Activity Action: Show screen that let user enable a Credential Manager provider. * <p> - * Input: Intent's data URI set with an application name, using the + * Input: Intent's data URI set with an application package name, using the * "package" schema (like "package:com.my.app"). * * <p> diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig index 02c63db2c8a6..155a3e483d82 100644 --- a/core/java/android/text/flags/flags.aconfig +++ b/core/java/android/text/flags/flags.aconfig @@ -240,3 +240,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "dont_break_email_in_nobreak_tag" + namespace: "text" + description: "Prevent line break inside email." + bug: "350691716" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 5c10db19b403..b796e0b1c429 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -104,12 +104,23 @@ public class InsetsSource implements Parcelable { */ public static final int FLAG_ANIMATE_RESIZING = 1 << 3; + /** + * Controls whether the {@link WindowInsets.Type#captionBar()} insets provided by this source + * should always be forcibly consumed. Unlike with {@link #FLAG_FORCE_CONSUMING}, when this + * flag is used the caption bar will be consumed even when the bar is requested to be visible. + * + * Note: this flag does not take effect when the window applies + * {@link WindowInsetsController.Appearance#APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND}. + */ + public static final int FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR = 1 << 4; + @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_SUPPRESS_SCRIM, FLAG_INSETS_ROUNDED_CORNER, FLAG_FORCE_CONSUMING, FLAG_ANIMATE_RESIZING, + FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR }) public @interface Flags {} @@ -555,6 +566,9 @@ public class InsetsSource implements Parcelable { if ((flags & FLAG_ANIMATE_RESIZING) != 0) { joiner.add("ANIMATE_RESIZING"); } + if ((flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) { + joiner.add("FORCE_CONSUMING_OPAQUE_CAPTION_BAR"); + } return joiner.toString(); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index bbd9acfd4cd7..6b4340a02edc 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -19,6 +19,7 @@ package android.view; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.util.SequenceUtils.getInitSeq; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.InsetsStateProto.DISPLAY_CUTOUT; import static android.view.InsetsStateProto.DISPLAY_FRAME; @@ -54,6 +55,7 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.Objects; @@ -131,18 +133,25 @@ public class InsetsState implements Parcelable { final Rect relativeFrame = new Rect(frame); final Rect relativeFrameMax = new Rect(frame); @InsetsType int forceConsumingTypes = 0; + boolean forceConsumingOpaqueCaptionBar = false; @InsetsType int suppressScrimTypes = 0; final Rect[][] typeBoundingRectsMap = new Rect[Type.SIZE][]; final Rect[][] typeMaxBoundingRectsMap = new Rect[Type.SIZE][]; for (int i = mSources.size() - 1; i >= 0; i--) { final InsetsSource source = mSources.valueAt(i); final @InsetsType int type = source.getType(); + final @InsetsSource.Flags int flags = source.getFlags(); - if ((source.getFlags() & InsetsSource.FLAG_FORCE_CONSUMING) != 0) { + if ((flags & InsetsSource.FLAG_FORCE_CONSUMING) != 0) { forceConsumingTypes |= type; } - if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) { + if (Flags.enableCaptionCompatInsetForceConsumptionAlways() + && (flags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) { + forceConsumingOpaqueCaptionBar = true; + } + + if ((flags & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) { suppressScrimTypes |= type; } @@ -177,7 +186,8 @@ public class InsetsState implements Parcelable { } return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, - forceConsumingTypes, suppressScrimTypes, calculateRelativeCutout(frame), + forceConsumingTypes, forceConsumingOpaqueCaptionBar, suppressScrimTypes, + calculateRelativeCutout(frame), calculateRelativeRoundedCorners(frame), calculateRelativePrivacyIndicatorBounds(frame), calculateRelativeDisplayShape(frame), diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a23e3839c348..64c77661d9f4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11004,6 +11004,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, ? afm.isAutofillable(this) : false; } + /** + * Returns whether the view is autofillable. + * + * @return whether the view is autofillable, and should send out autofill request to provider. + */ private boolean isAutofillable() { if (DBG) { Log.d(VIEW_LOG_TAG, "isAutofillable() entered."); @@ -27631,6 +27636,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } + + /** + * Performs the traversal to find views that are autofillable. + * Autofillable views are added to the provided list. + * + * <strong>Note:</strong>This method does not stop at the root namespace + * boundary. + * + * @param autofillableViews The output list of autofillable Views. + * @hide + */ + public void findAutofillableViewsByTraversal(@NonNull List<View> autofillableViews) { + if (isAutofillable()) { + autofillableViews.add(this); + } + } + /** * Look for a child view with the given tag. If this view has the given * tag, return this view. @@ -30597,6 +30619,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + // Note that if the function returns true, it indicates aapt did not generate this id. + // However false value does not indicate that aapt did generated this id. private static boolean isViewIdGenerated(int id) { return (id & 0xFF000000) == 0 && (id & 0x00FFFFFF) != 0; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index ceaca2257af4..6f8838619808 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1500,6 +1500,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } + /** @hide */ + @Override + public void findAutofillableViewsByTraversal(@NonNull List<View> autofillableViews) { + super.findAutofillableViewsByTraversal(autofillableViews); + + final int childrenCount = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child = children[i]; + child.findAutofillableViewsByTraversal(autofillableViews); + } + } + @Override public void dispatchWindowFocusChanged(boolean hasFocus) { super.dispatchWindowFocusChanged(hasFocus); diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 987c8c8213f3..e3ea6b229d64 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -97,6 +97,7 @@ public final class WindowInsets { private final int mFrameHeight; private final @InsetsType int mForceConsumingTypes; + private final boolean mForceConsumingOpaqueCaptionBar; private final @InsetsType int mSuppressScrimTypes; private final boolean mSystemWindowInsetsConsumed; private final boolean mStableInsetsConsumed; @@ -123,7 +124,7 @@ public final class WindowInsets { static { CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null), - createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, 0, null, + createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, false, 0, null, null, null, null, systemBars(), false, null, null, 0, 0); } @@ -144,6 +145,7 @@ public final class WindowInsets { boolean[] typeVisibilityMap, boolean isRound, @InsetsType int forceConsumingTypes, + boolean forceConsumingOpaqueCaptionBar, @InsetsType int suppressScrimTypes, DisplayCutout displayCutout, RoundedCorners roundedCorners, @@ -166,6 +168,7 @@ public final class WindowInsets { mTypeVisibilityMap = typeVisibilityMap; mIsRound = isRound; mForceConsumingTypes = forceConsumingTypes; + mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar; mSuppressScrimTypes = suppressScrimTypes; mCompatInsetsTypes = compatInsetsTypes; mCompatIgnoreVisibility = compatIgnoreVisibility; @@ -196,7 +199,9 @@ public final class WindowInsets { this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap, src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap, src.mTypeVisibilityMap, src.mIsRound, - src.mForceConsumingTypes, src.mSuppressScrimTypes, + src.mForceConsumingTypes, + src.mForceConsumingOpaqueCaptionBar, + src.mSuppressScrimTypes, displayCutoutCopyConstructorArgument(src), src.mRoundedCorners, src.mPrivacyIndicatorBounds, @@ -257,7 +262,7 @@ public final class WindowInsets { /** @hide */ @UnsupportedAppUsage public WindowInsets(Rect systemWindowInsets) { - this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, 0, + this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, false, 0, null, null, null, null, systemBars(), false /* compatIgnoreVisibility */, new Rect[SIZE][], null, 0, 0); } @@ -675,10 +680,10 @@ public final class WindowInsets { return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, - null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, - mCompatInsetsTypes, mCompatIgnoreVisibility, - mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, null /* displayCutout */, mRoundedCorners, + mPrivacyIndicatorBounds, mDisplayShape, mCompatInsetsTypes, + mCompatIgnoreVisibility, mSystemWindowInsetsConsumed ? null : mTypeBoundingRectsMap, mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap, mFrameWidth, mFrameHeight); } @@ -729,7 +734,8 @@ public final class WindowInsets { public WindowInsets consumeSystemWindowInsets() { return new WindowInsets(null, null, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, // If the system window insets types contain displayCutout, we should also consume // it. (mCompatInsetsTypes & displayCutout()) != 0 @@ -1024,6 +1030,13 @@ public final class WindowInsets { /** * @hide */ + public boolean isForceConsumingOpaqueCaptionBar() { + return mForceConsumingOpaqueCaptionBar; + } + + /** + * @hide + */ public @InsetsType int getSuppressScrimTypes() { return mSuppressScrimTypes; } @@ -1058,6 +1071,8 @@ public final class WindowInsets { result.append("\n "); result.append("forceConsumingTypes=" + Type.toString(mForceConsumingTypes)); result.append("\n "); + result.append("forceConsumingOpaqueCaptionBar=" + mForceConsumingOpaqueCaptionBar); + result.append("\n "); result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes)); result.append("\n "); result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes)); @@ -1180,7 +1195,8 @@ public final class WindowInsets { ? null : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom), mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, mDisplayCutoutConsumed ? null : mDisplayCutout == null @@ -1214,6 +1230,7 @@ public final class WindowInsets { return mIsRound == that.mIsRound && mForceConsumingTypes == that.mForceConsumingTypes + && mForceConsumingOpaqueCaptionBar == that.mForceConsumingOpaqueCaptionBar && mSuppressScrimTypes == that.mSuppressScrimTypes && mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed && mStableInsetsConsumed == that.mStableInsetsConsumed @@ -1235,9 +1252,9 @@ public final class WindowInsets { public int hashCode() { return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap), Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners, - mForceConsumingTypes, mSuppressScrimTypes, mSystemWindowInsetsConsumed, - mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds, - mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap), + mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, mSuppressScrimTypes, + mSystemWindowInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed, + mPrivacyIndicatorBounds, mDisplayShape, Arrays.deepHashCode(mTypeBoundingRectsMap), Arrays.deepHashCode(mTypeMaxBoundingRectsMap), mFrameWidth, mFrameHeight); } @@ -1367,6 +1384,7 @@ public final class WindowInsets { private boolean mIsRound; private @InsetsType int mForceConsumingTypes; + private boolean mForceConsumingOpaqueCaptionBar; private @InsetsType int mSuppressScrimTypes; private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds(); @@ -1399,6 +1417,7 @@ public final class WindowInsets { mRoundedCorners = insets.mRoundedCorners; mIsRound = insets.mIsRound; mForceConsumingTypes = insets.mForceConsumingTypes; + mForceConsumingOpaqueCaptionBar = insets.mForceConsumingOpaqueCaptionBar; mSuppressScrimTypes = insets.mSuppressScrimTypes; mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds; mDisplayShape = insets.mDisplayShape; @@ -1687,6 +1706,13 @@ public final class WindowInsets { /** @hide */ @NonNull + public Builder setForceConsumingOpaqueCaptionBar(boolean forceConsumingOpaqueCaptionBar) { + mForceConsumingOpaqueCaptionBar = forceConsumingOpaqueCaptionBar; + return this; + } + + /** @hide */ + @NonNull public Builder setSuppressScrimTypes(@InsetsType int suppressScrimTypes) { mSuppressScrimTypes = suppressScrimTypes; return this; @@ -1765,9 +1791,9 @@ public final class WindowInsets { public WindowInsets build() { return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap, mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap, - mIsRound, mForceConsumingTypes, mSuppressScrimTypes, mDisplayCutout, - mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(), - false /* compatIgnoreVisibility */, + mIsRound, mForceConsumingTypes, mForceConsumingOpaqueCaptionBar, + mSuppressScrimTypes, mDisplayCutout, mRoundedCorners, mPrivacyIndicatorBounds, + mDisplayShape, systemBars(), false /* compatIgnoreVisibility */, mSystemInsetsConsumed ? null : mTypeBoundingRectsMap, mStableInsetsConsumed ? null : mTypeMaxBoundingRectsMap, mFrameWidth, mFrameHeight); diff --git a/core/java/android/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java index 2f7adaa29fed..d505c733b3e9 100644 --- a/core/java/android/view/autofill/AutofillClientController.java +++ b/core/java/android/view/autofill/AutofillClientController.java @@ -39,6 +39,7 @@ import android.view.WindowManagerGlobal; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * A controller to manage the autofill requests for the {@link Activity}. @@ -71,6 +72,7 @@ public final class AutofillClientController implements AutofillManager.AutofillC private AutofillPopupWindow mAutofillPopupWindow; private boolean mAutoFillResetNeeded; private boolean mAutoFillIgnoreFirstResumePause; + private Boolean mRelayoutFix; /** * AutofillClientController constructor. @@ -86,6 +88,18 @@ public final class AutofillClientController implements AutofillManager.AutofillC return mAutofillManager; } + /** + * Whether to apply relayout fixes. + * + * @hide + */ + public boolean isRelayoutFixEnabled() { + if (mRelayoutFix == null) { + mRelayoutFix = getAutofillManager().isRelayoutFixEnabled(); + } + return mRelayoutFix; + } + // ------------------ Called for Activity events ------------------ /** @@ -119,7 +133,54 @@ public final class AutofillClientController implements AutofillManager.AutofillC * Called when the {@link Activity#onResume()} is called. */ public void onActivityResumed() { + if (Helper.sVerbose) { + Log.v(TAG, "onActivityResumed()"); + } + if (isRelayoutFixEnabled()) { + // Do nothing here. We'll handle it in onActivityPostResumed() + return; + } + if (Helper.sVerbose) { + Log.v(TAG, "onActivityResumed(): Relayout fix not enabled"); + } + forResume(); + } + + /** + * Called when the {@link Activity#onPostResume()} is called. + */ + public void onActivityPostResumed() { + if (Helper.sVerbose) { + Log.v(TAG, "onActivityPostResumed()"); + } + if (!isRelayoutFixEnabled()) { + return; + } + if (Helper.sVerbose) { + Log.v(TAG, "onActivityPostResumed(): Relayout fix enabled"); + } + forResume(); + } + + /** + * Code to execute when an app has resumed (or is about to resume) + */ + private void forResume() { enableAutofillCompatibilityIfNeeded(); + boolean relayoutFix = isRelayoutFixEnabled(); + if (relayoutFix) { + if (getAutofillManager().shouldRetryFill()) { + if (Helper.sVerbose) { + Log.v(TAG, "forResume(): Autofill potential relayout. Retrying fill."); + } + getAutofillManager().attemptRefill(); + } else { + if (Helper.sVerbose) { + Log.v(TAG, "forResume(): Not attempting refill."); + } + } + } + if (mAutoFillResetNeeded) { if (!mAutoFillIgnoreFirstResumePause) { View focus = mActivity.getCurrentFocus(); @@ -131,7 +192,16 @@ public final class AutofillClientController implements AutofillManager.AutofillC // ViewRootImpl.performTraversals() changes window visibility to VISIBLE. // So we cannot call View.notifyEnterOrExited() which will do nothing // when View.isVisibleToUser() is false. - getAutofillManager().notifyViewEntered(focus); + if (relayoutFix && getAutofillManager().isAuthenticationPending()) { + if (Helper.sVerbose) { + Log.v(TAG, "forResume(): ignoring focus due to auth pending"); + } + } else { + if (Helper.sVerbose) { + Log.v(TAG, "forResume(): notifyViewEntered"); + } + getAutofillManager().notifyViewEntered(focus); + } } } } @@ -427,6 +497,22 @@ public final class AutofillClientController implements AutofillManager.AutofillC } @Override + public List<View> autofillClientFindAutofillableViewsByTraversal() { + final ArrayList<View> views = new ArrayList<>(); + final ArrayList<ViewRootImpl> roots = + WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken()); + + for (int rootNum = 0; rootNum < roots.size(); rootNum++) { + final View rootView = roots.get(rootNum).getView(); + + if (rootView != null) { + rootView.findAutofillableViewsByTraversal(views); + } + } + return views; + } + + @Override public boolean autofillClientIsFillUiShowing() { return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing(); } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 515ed0e8f6af..02a86c9eecb3 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -874,6 +874,13 @@ public final class AutofillManager { @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId); /** + * Finds all the autofillable views on the screen. + * + * @return The list of views that are autofillable. + */ + List<View> autofillClientFindAutofillableViewsByTraversal(); + + /** * Runs the specified action on the UI thread. */ void autofillClientRunOnUiThread(Runnable action); @@ -1498,6 +1505,37 @@ public final class AutofillManager { } /** + * Called to know whether authentication was pending. + * @hide + */ + public boolean isAuthenticationPending() { + return mState == STATE_PENDING_AUTHENTICATION; + } + + /** + * Called to check if we should retry fill. + * Useful for knowing whether to attempt refill after relayout. + * + * @hide + */ + public boolean shouldRetryFill() { + // TODO: Implement in follow-up cl + return false; + } + + /** + * Called when a potential relayout may have occurred. + * + * @return whether refill was done. True if refill was done partially or fully. + * @hide + */ + public boolean attemptRefill() { + Log.i(TAG, "Attempting refill"); + // TODO: Implement in follow-up cl + return false; + } + + /** * Called when a {@link View} that supports autofill is entered. * * @param view {@link View} that was entered. diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 0dadbe374aa4..eb3581717637 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -8360,7 +8360,7 @@ public class RemoteViews implements Parcelable, Filter { } } - private interface PendingResources<T> { + interface PendingResources<T> { T create(Context context, Resources appResources, HierarchyRootData rootData, int depth) throws Exception; } diff --git a/core/java/android/widget/RemoteViewsSerializers.java b/core/java/android/widget/RemoteViewsSerializers.java new file mode 100644 index 000000000000..600fea4a0bb8 --- /dev/null +++ b/core/java/android/widget/RemoteViewsSerializers.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.widget; + +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BlendMode; +import android.graphics.drawable.Icon; +import android.util.Log; +import android.util.LongSparseArray; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoUtils; + +import androidx.annotation.NonNull; + +import java.io.ByteArrayOutputStream; +import java.util.function.Function; + +/** + * This class provides serialization for certain types used within RemoteViews. + * + * @hide + */ +public class RemoteViewsSerializers { + private static final String TAG = "RemoteViews"; + + /** + * Write Icon to proto. + */ + public static void writeIconToProto(@NonNull ProtoOutputStream out, + @NonNull Resources appResources, @NonNull Icon icon) { + if (icon.getTintList() != null) { + final long token = out.start(RemoteViewsProto.Icon.TINT_LIST); + icon.getTintList().writeToProto(out); + out.end(token); + } + out.write(RemoteViewsProto.Icon.BLEND_MODE, BlendMode.toValue(icon.getTintBlendMode())); + switch (icon.getType()) { + case Icon.TYPE_BITMAP: + final ByteArrayOutputStream bitmapBytes = new ByteArrayOutputStream(); + icon.getBitmap().compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bitmapBytes); + out.write(RemoteViewsProto.Icon.BITMAP, bitmapBytes.toByteArray()); + break; + case Icon.TYPE_ADAPTIVE_BITMAP: + final ByteArrayOutputStream adaptiveBitmapBytes = new ByteArrayOutputStream(); + icon.getBitmap().compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, + adaptiveBitmapBytes); + out.write(RemoteViewsProto.Icon.ADAPTIVE_BITMAP, adaptiveBitmapBytes.toByteArray()); + break; + case Icon.TYPE_RESOURCE: + out.write(RemoteViewsProto.Icon.RESOURCE, + appResources.getResourceName(icon.getResId())); + break; + case Icon.TYPE_DATA: + out.write(RemoteViewsProto.Icon.DATA, icon.getDataBytes()); + break; + case Icon.TYPE_URI: + out.write(RemoteViewsProto.Icon.URI, icon.getUriString()); + break; + case Icon.TYPE_URI_ADAPTIVE_BITMAP: + out.write(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP, icon.getUriString()); + break; + default: + Log.e(TAG, "Tried to serialize unknown Icon type " + icon.getType()); + } + } + + /** + * Create Icon from proto. + */ + @NonNull + public static Function<Resources, Icon> createIconFromProto(@NonNull ProtoInputStream in) + throws Exception { + final LongSparseArray<Object> values = new LongSparseArray<>(); + while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (in.getFieldNumber()) { + case (int) RemoteViewsProto.Icon.BLEND_MODE: + values.put(RemoteViewsProto.Icon.BLEND_MODE, + in.readInt(RemoteViewsProto.Icon.BLEND_MODE)); + break; + case (int) RemoteViewsProto.Icon.TINT_LIST: + final long tintListToken = in.start(RemoteViewsProto.Icon.TINT_LIST); + values.put(RemoteViewsProto.Icon.TINT_LIST, ColorStateList.createFromProto(in)); + in.end(tintListToken); + break; + case (int) RemoteViewsProto.Icon.BITMAP: + byte[] bitmapData = in.readBytes(RemoteViewsProto.Icon.BITMAP); + values.put(RemoteViewsProto.Icon.BITMAP, + BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length)); + break; + case (int) RemoteViewsProto.Icon.ADAPTIVE_BITMAP: + final byte[] bitmapAdaptiveData = in.readBytes( + RemoteViewsProto.Icon.ADAPTIVE_BITMAP); + values.put(RemoteViewsProto.Icon.ADAPTIVE_BITMAP, + BitmapFactory.decodeByteArray(bitmapAdaptiveData, 0, + bitmapAdaptiveData.length)); + break; + case (int) RemoteViewsProto.Icon.RESOURCE: + values.put(RemoteViewsProto.Icon.RESOURCE, + in.readString(RemoteViewsProto.Icon.RESOURCE)); + break; + case (int) RemoteViewsProto.Icon.DATA: + values.put(RemoteViewsProto.Icon.DATA, + in.readBytes(RemoteViewsProto.Icon.DATA)); + break; + case (int) RemoteViewsProto.Icon.URI: + values.put(RemoteViewsProto.Icon.URI, in.readString(RemoteViewsProto.Icon.URI)); + break; + case (int) RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP: + values.put(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP, + in.readString(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP)); + break; + default: + Log.w(TAG, "Unhandled field while reading Icon proto!\n" + + ProtoUtils.currentFieldToString(in)); + } + } + + return (resources) -> { + final int blendMode = (int) values.get(RemoteViewsProto.Icon.BLEND_MODE, -1); + final ColorStateList tintList = (ColorStateList) values.get( + RemoteViewsProto.Icon.TINT_LIST); + final Bitmap bitmap = (Bitmap) values.get(RemoteViewsProto.Icon.BITMAP); + final Bitmap bitmapAdaptive = (Bitmap) values.get( + RemoteViewsProto.Icon.ADAPTIVE_BITMAP); + final String resName = (String) values.get(RemoteViewsProto.Icon.RESOURCE); + final int resource = resName != null ? resources.getIdentifier(resName, /* defType= */ + null, + /* defPackage= */ null) : -1; + final byte[] data = (byte[]) values.get(RemoteViewsProto.Icon.DATA); + final String uri = (String) values.get(RemoteViewsProto.Icon.URI); + final String uriAdaptive = (String) values.get( + RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP); + Icon icon; + if (bitmap != null) { + icon = Icon.createWithBitmap(bitmap); + } else if (bitmapAdaptive != null) { + icon = Icon.createWithAdaptiveBitmap(bitmapAdaptive); + } else if (resource != -1) { + icon = Icon.createWithResource(resources, resource); + } else if (data != null) { + icon = Icon.createWithData(data, 0, data.length); + } else if (uri != null) { + icon = Icon.createWithContentUri(uri); + } else if (uriAdaptive != null) { + icon = Icon.createWithAdaptiveBitmapContentUri(uriAdaptive); + } else { + // Either this Icon has no data or is of an unknown type. + return null; + } + + if (tintList != null) { + icon.setTintList(tintList); + } + if (blendMode != -1) { + icon.setTintBlendMode(BlendMode.fromValue(blendMode)); + } + return icon; + }; + } +} diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 1a660be64c99..3b25109fa845 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -477,7 +477,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } else if (mMinuteSpinnerInput.hasFocus()) { inputMethodManager.hideSoftInputFromView(mMinuteSpinnerInput, 0); mMinuteSpinnerInput.clearFocus(); - } else if (mAmPmSpinnerInput.hasFocus()) { + } else if (mAmPmSpinnerInput != null && mAmPmSpinnerInput.hasFocus()) { inputMethodManager.hideSoftInputFromView(mAmPmSpinnerInput, 0); mAmPmSpinnerInput.clearFocus(); } diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 7f48c42c1d89..76989f9ea2f4 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -180,6 +180,17 @@ flag { } flag { + name: "use_tasks_dim_only" + namespace: "windowing_frontend" + description: "Only use the task's dim and reparent it to the display area when needed instead of coordinating multiple dimmers" + bug: "352522056" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "release_snapshot_aggressively" namespace: "windowing_frontend" description: "Actively release task snapshot memory" diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index bf5df031e3ef..53ef49bd3f65 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -317,6 +317,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, name, name, (int) mBeginVsyncId); markEvent("FT#beginVsync", mBeginVsyncId); markEvent("FT#layerId", mSurfaceControl.getLayerId()); + markCujUiThread(); mJankDataListenerRegistration = mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); if (!mSurfaceOnly) { @@ -433,6 +434,13 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } } + private void markCujUiThread() { + if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) { + // This is being called from the CUJ ui thread. + Trace.instant(Trace.TRACE_TAG_APP, mConfig.getSessionName() + "#UIThread"); + } + } + private void notifyCujEvent(String action, @Reasons int reason) { if (mListener == null) return; mListener.onCujEvents(this, action, reason); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 87e22ed42cbd..48283930595d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -29,6 +29,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; +import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -36,6 +37,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.flags.Flags.customizableWindowHeaders; import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; @@ -226,6 +228,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private boolean mLastHasLeftStableInset = false; private int mLastWindowFlags = 0; private @InsetsType int mLastForceConsumingTypes = 0; + private boolean mLastForceConsumingOpaqueCaptionBar = false; private @InsetsType int mLastSuppressScrimTypes = 0; private int mRootScrollY = 0; @@ -1068,8 +1071,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind WindowManager.LayoutParams attrs = mWindow.getAttributes(); int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); + final ViewRootImpl viewRoot = getViewRootImpl(); final WindowInsetsController controller = getWindowInsetsController(); final @InsetsType int requestedVisibleTypes = controller.getRequestedVisibleTypes(); + final @Appearance int appearance = viewRoot != null + ? viewRoot.mWindowAttributes.insetsFlags.appearance + : controller.getSystemBarsAppearance(); // IME is an exceptional floating window that requires color view. final boolean isImeWindow = @@ -1080,13 +1087,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; mLastWindowFlags = attrs.flags; - final ViewRootImpl viewRoot = getViewRootImpl(); - final @Appearance int appearance = viewRoot != null - ? viewRoot.mWindowAttributes.insetsFlags.appearance - : controller.getSystemBarsAppearance(); - if (insets != null) { mLastForceConsumingTypes = insets.getForceConsumingTypes(); + mLastForceConsumingOpaqueCaptionBar = insets.isForceConsumingOpaqueCaptionBar(); final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags, getResources().getConfiguration().windowConfiguration.getActivityType(), @@ -1209,16 +1212,20 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final boolean hideCaptionBar = fullscreen || (requestedVisibleTypes & WindowInsets.Type.captionBar()) == 0; - final boolean consumingCaptionBar = - ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0 + final boolean consumingCaptionBar = Flags.enableCaptionCompatInsetForceConsumption() + && ((mLastForceConsumingTypes & WindowInsets.Type.captionBar()) != 0 && hideCaptionBar); - final int consumedTop; - if (Flags.enableCaptionCompatInsetForceConsumption()) { - consumedTop = (consumingStatusBar || consumingCaptionBar) ? mLastTopInset : 0; - } else { - consumedTop = consumingStatusBar ? mLastTopInset : 0; - } + final boolean isOpaqueCaptionBar = customizableWindowHeaders() + && (appearance & APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) == 0; + final boolean consumingOpaqueCaptionBar = + Flags.enableCaptionCompatInsetForceConsumptionAlways() + && mLastForceConsumingOpaqueCaptionBar + && isOpaqueCaptionBar; + + final int consumedTop = + (consumingStatusBar || consumingCaptionBar || consumingOpaqueCaptionBar) + ? mLastTopInset : 0; int consumedRight = consumingNavBar ? mLastRightInset : 0; int consumedBottom = consumingNavBar ? mLastBottomInset : 0; int consumedLeft = consumingNavBar ? mLastLeftInset : 0; diff --git a/core/java/com/android/internal/policy/SystemBarUtils.java b/core/java/com/android/internal/policy/SystemBarUtils.java index efa369715373..4ed15faf8b89 100644 --- a/core/java/com/android/internal/policy/SystemBarUtils.java +++ b/core/java/com/android/internal/policy/SystemBarUtils.java @@ -92,4 +92,11 @@ public final class SystemBarUtils { // Equals to status bar height if status bar height is bigger. return Math.max(defaultSize, statusBarHeight); } + + /** + * Gets the taskbar frame height. + */ + public static int getTaskbarHeight(Resources res) { + return res.getDimensionPixelSize(R.dimen.taskbar_frame_height); + } } diff --git a/core/proto/android/widget/remoteviews.proto b/core/proto/android/widget/remoteviews.proto index f08ea1b6f092..37d1c5b03ee5 100644 --- a/core/proto/android/widget/remoteviews.proto +++ b/core/proto/android/widget/remoteviews.proto @@ -21,6 +21,7 @@ option java_multiple_files = true; package android.widget; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/content/res/color_state_list.proto"; /** * An android.widget.RemoteViews object. This is used by RemoteViews.createPreviewFromProto @@ -71,6 +72,23 @@ message RemoteViewsProto { optional int32 view_type_count = 4; optional bool attached = 5; } + + /** + * An android.graphics.drawable Icon. + */ + message Icon { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 blend_mode = 1; + optional android.content.res.ColorStateListProto tint_list = 2; + oneof icon { + bytes bitmap = 3; + string resource = 4; + bytes data = 5; + string uri = 6; + string uri_adaptive_bitmap = 7; + bytes adaptive_bitmap = 8; + }; + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 193836ed9b15..f3dac2313c91 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2611,6 +2611,14 @@ <permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" android:protectionLevel="signature" /> + <!-- @SystemApi Allows access to perform vendor effects in the vibrator. + <p>Protection level: signature + @FlaggedApi("android.os.vibrator.vendor_vibration_effects") + @hide + --> + <permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows access to the vibrator state. <p>Protection level: signature @hide diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml index 7a707c0a6cdc..cc7b891b9377 100644 --- a/core/res/res/values/config_device_idle.xml +++ b/core/res/res/values/config_device_idle.xml @@ -28,7 +28,7 @@ <integer name="device_idle_flex_time_short_ms">60000</integer> <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT --> - <integer name="device_idle_light_after_inactive_to_ms">60000</integer> + <integer name="device_idle_light_after_inactive_to_ms">240000</integer> <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT --> <integer name="device_idle_light_idle_to_ms">300000</integer> @@ -67,10 +67,10 @@ <integer name="device_idle_min_deep_maintenance_time_ms">30000</integer> <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT --> - <integer name="device_idle_inactive_to_ms">60000</integer> + <integer name="device_idle_inactive_to_ms">15000</integer> <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT --> - <integer name="device_idle_sensing_to_ms">30000</integer> + <integer name="device_idle_sensing_to_ms">15000</integer> <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT --> <integer name="device_idle_locating_to_ms">15000</integer> @@ -79,13 +79,13 @@ <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item> <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT --> - <integer name="device_idle_motion_inactive_to_ms">600000</integer> + <integer name="device_idle_motion_inactive_to_ms">30000</integer> <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT_FLEX --> <integer name="device_idle_motion_inactive_to_flex_ms">60000</integer> <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT --> - <integer name="device_idle_idle_after_inactive_to_ms">60000</integer> + <integer name="device_idle_idle_after_inactive_to_ms">15000</integer> <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT --> <integer name="device_idle_idle_pending_to_ms">300000</integer> @@ -100,7 +100,7 @@ <integer name="device_idle_quick_doze_delay_to_ms">60000</integer> <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT --> - <integer name="device_idle_idle_to_ms">900000</integer> + <integer name="device_idle_idle_to_ms">3600000</integer> <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT --> <integer name="device_idle_max_idle_to_ms">21600000</integer> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index f5affd39b940..24f6ceaf786c 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -254,8 +254,8 @@ public class ActivityThreadTest { try { // Send process level config change. ClientTransaction transaction = newTransaction(activityThread); - transaction.addTransactionItem(ConfigurationChangeItem.obtain( - newConfig, DEVICE_ID_INVALID)); + transaction.addTransactionItem( + new ConfigurationChangeItem(newConfig, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -271,7 +271,7 @@ public class ActivityThreadTest { newConfig.seq++; newConfig.smallestScreenWidthDp++; transaction = newTransaction(activityThread); - transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain( + transaction.addTransactionItem(new ActivityConfigurationChangeItem( activity.getActivityToken(), newConfig, new ActivityWindowInfo())); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -474,16 +474,16 @@ public class ActivityThreadTest { activity.mTestLatch = new CountDownLatch(1); ClientTransaction transaction = newTransaction(activityThread); - transaction.addTransactionItem(ConfigurationChangeItem.obtain( - processConfigLandscape, DEVICE_ID_INVALID)); + transaction.addTransactionItem( + new ConfigurationChangeItem(processConfigLandscape, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); transaction = newTransaction(activityThread); - transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain( + transaction.addTransactionItem(new ActivityConfigurationChangeItem( activity.getActivityToken(), activityConfigLandscape, new ActivityWindowInfo())); - transaction.addTransactionItem(ConfigurationChangeItem.obtain( - processConfigPortrait, DEVICE_ID_INVALID)); - transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain( + transaction.addTransactionItem( + new ConfigurationChangeItem(processConfigPortrait, DEVICE_ID_INVALID)); + transaction.addTransactionItem(new ActivityConfigurationChangeItem( activity.getActivityToken(), activityConfigPortrait, new ActivityWindowInfo())); appThread.scheduleTransaction(transaction); @@ -847,7 +847,7 @@ public class ActivityThreadTest { final ActivityWindowInfo newInfo = new ActivityWindowInfo(); newInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000), new Rect(0, 0, 1000, 1000)); - final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain( + final ActivityRelaunchItem relaunchItem = new ActivityRelaunchItem( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, newInfo); @@ -881,7 +881,7 @@ public class ActivityThreadTest { final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, taskBounds, taskFragmentBounds); final ActivityConfigurationChangeItem activityConfigurationChangeItem = - ActivityConfigurationChangeItem.obtain( + new ActivityConfigurationChangeItem( activity.getActivityToken(), config, activityWindowInfo); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(activityConfigurationChangeItem); @@ -898,7 +898,7 @@ public class ActivityThreadTest { new ActivityWindowInfo(activityWindowInfo); config.seq++; final ActivityConfigurationChangeItem activityConfigurationChangeItem2 = - ActivityConfigurationChangeItem.obtain( + new ActivityConfigurationChangeItem( activity.getActivityToken(), config, activityWindowInfo2); final ClientTransaction transaction2 = newTransaction(activity); transaction2.addTransactionItem(activityConfigurationChangeItem2); @@ -978,12 +978,12 @@ public class ActivityThreadTest { } else { activityWindowInfo = record.getActivityWindowInfo(); } - final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain( + final ClientTransactionItem callbackItem = new ActivityRelaunchItem( activity.getActivityToken(), null, null, 0, new MergedConfiguration(currentConfig, currentConfig), false /* preserveWindow */, activityWindowInfo); final ResumeActivityItem resumeStateRequest = - ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */, + new ResumeActivityItem(activity.getActivityToken(), true /* isForward */, false /* shouldSendCompatFakeFocus*/); final ClientTransaction transaction = newTransaction(activity); @@ -996,7 +996,7 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newResumeTransaction(@NonNull Activity activity) { final ResumeActivityItem resumeStateRequest = - ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */, + new ResumeActivityItem(activity.getActivityToken(), true /* isForward */, false /* shouldSendCompatFakeFocus */); final ClientTransaction transaction = newTransaction(activity); @@ -1007,8 +1007,7 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newStopTransaction(@NonNull Activity activity) { - final StopActivityItem stopStateRequest = StopActivityItem.obtain( - activity.getActivityToken()); + final StopActivityItem stopStateRequest = new StopActivityItem(activity.getActivityToken()); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(stopStateRequest); @@ -1019,7 +1018,7 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity, @NonNull Configuration config) { - final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain( + final ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem( activity.getActivityToken(), config, new ActivityWindowInfo()); final ClientTransaction transaction = newTransaction(activity); @@ -1031,8 +1030,7 @@ public class ActivityThreadTest { @NonNull private static ClientTransaction newNewIntentTransaction(@NonNull Activity activity, @NonNull List<ReferrerIntent> intents, boolean resume) { - final NewIntentItem item = NewIntentItem.obtain(activity.getActivityToken(), intents, - resume); + final NewIntentItem item = new NewIntentItem(activity.getActivityToken(), intents, resume); final ClientTransaction transaction = newTransaction(activity); transaction.addTransactionItem(item); diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java index ff30f190bb05..f023196250e5 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java +++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java @@ -21,13 +21,17 @@ import static android.view.Display.DEFAULT_DISPLAY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.app.Activity; +import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ClientTransactionHandler; import android.content.res.Configuration; @@ -134,9 +138,44 @@ public class ClientTransactionItemTest { } @Test + public void testResumeActivityItem_preExecute_withProcState_updatesProcessState() { + final ResumeActivityItem item = new ResumeActivityItem(mActivityToken, + ActivityManager.PROCESS_STATE_TOP /* procState */, + true /* isForward */, + false /* shouldSendCompatFakeFocus*/); + + item.preExecute(mHandler); + + verify(mHandler).updateProcessState(ActivityManager.PROCESS_STATE_TOP, false); + } + + @Test + public void testResumeActivityItem_preExecute_withUnknownProcState_skipsProcessStateUpdate() { + final ResumeActivityItem item = new ResumeActivityItem(mActivityToken, + ActivityManager.PROCESS_STATE_UNKNOWN /* procState */, + true /* isForward */, + false /* shouldSendCompatFakeFocus*/); + + item.preExecute(mHandler); + + verify(mHandler, never()).updateProcessState(anyInt(), anyBoolean()); + } + + @Test + public void testResumeActivityItem_preExecute_withoutProcState_skipsProcessStateUpdate() { + final ResumeActivityItem item = new ResumeActivityItem(mActivityToken, + true /* isForward */, + false /* shouldSendCompatFakeFocus*/); + + item.preExecute(mHandler); + + verify(mHandler, never()).updateProcessState(anyInt(), anyBoolean()); + } + + @Test public void testWindowContextInfoChangeItem_execute() { - final WindowContextInfoChangeItem item = WindowContextInfoChangeItem - .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY); + final WindowContextInfoChangeItem item = new WindowContextInfoChangeItem(mWindowClientToken, + mConfiguration, DEFAULT_DISPLAY); item.execute(mHandler, mPendingActions); @@ -146,8 +185,8 @@ public class ClientTransactionItemTest { @Test public void testWindowContextWindowRemovalItem_execute() { - final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain( - mWindowClientToken); + final WindowContextWindowRemovalItem item = + new WindowContextWindowRemovalItem(mWindowClientToken); item.execute(mHandler, mPendingActions); diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java deleted file mode 100644 index 40f6aa0b8f79..000000000000 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.servertransaction; - -import static android.app.servertransaction.TestUtils.config; -import static android.app.servertransaction.TestUtils.mergedConfig; -import static android.app.servertransaction.TestUtils.referrerIntentList; -import static android.app.servertransaction.TestUtils.resultInfoList; - -import static org.junit.Assert.assertNotSame; - -import android.annotation.NonNull; -import android.app.ActivityOptions; -import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.PersistableBundle; -import android.platform.test.annotations.Presubmit; -import android.window.ActivityWindowInfo; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.function.Supplier; - -/** - * Tests for {@link ObjectPool}. - * - * <p>Build/Install/Run: - * atest FrameworksCoreTests:ObjectPoolTests - * - * <p>This test class is a part of Window Manager Service tests and specified in - * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class ObjectPoolTests { - - @Rule - public final MockitoRule mocks = MockitoJUnit.rule(); - - @Mock - private IBinder mActivityToken; - - // 1. Check if two obtained objects from pool are not the same. - // 2. Check if the state of the object is cleared after recycling. - // 3. Check if the same object is obtained from pool after recycling. - - @Test - public void testRecycleActivityConfigurationChangeItem() { - testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config(), - new ActivityWindowInfo())); - } - - @Test - public void testRecycleActivityResultItem() { - testRecycle(() -> ActivityResultItem.obtain(mActivityToken, resultInfoList())); - } - - @Test - public void testRecycleConfigurationChangeItem() { - testRecycle(() -> ConfigurationChangeItem.obtain(config(), 1)); - } - - @Test - public void testRecycleLaunchActivityItem() { - final IBinder activityToken = new Binder(); - final Intent intent = new Intent("action"); - final int ident = 57; - final ActivityInfo activityInfo = new ActivityInfo(); - activityInfo.flags = 42; - activityInfo.setMaxAspectRatio(2.4f); - activityInfo.launchToken = "token"; - activityInfo.applicationInfo = new ApplicationInfo(); - activityInfo.packageName = "packageName"; - activityInfo.name = "name"; - final Configuration overrideConfig = new Configuration(); - overrideConfig.assetsSeq = 5; - final String referrer = "referrer"; - final int procState = 4; - final Bundle bundle = new Bundle(); - bundle.putString("key", "value"); - final PersistableBundle persistableBundle = new PersistableBundle(); - persistableBundle.putInt("k", 4); - final IBinder assistToken = new Binder(); - final IBinder shareableActivityToken = new Binder(); - final int deviceId = 3; - final IBinder taskFragmentToken = new Binder(); - final IBinder initialCallerInfoAccessToken = new Binder(); - final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); - - testRecycle(() -> new LaunchActivityItemBuilder( - activityToken, intent, activityInfo) - .setIdent(ident) - .setCurConfig(config()) - .setOverrideConfig(overrideConfig) - .setReferrer(referrer) - .setProcState(procState) - .setState(bundle) - .setPersistentState(persistableBundle) - .setPendingResults(resultInfoList()) - .setPendingNewIntents(referrerIntentList()) - .setIsForward(true) - .setAssistToken(assistToken) - .setShareableActivityToken(shareableActivityToken) - .setTaskFragmentToken(taskFragmentToken) - .setDeviceId(deviceId) - .setInitialCallerInfoAccessToken(initialCallerInfoAccessToken) - .setActivityWindowInfo(activityWindowInfo) - .build()); - } - - @Test - public void testRecycleActivityRelaunchItem() { - testRecycle(() -> ActivityRelaunchItem.obtain(mActivityToken, - resultInfoList(), referrerIntentList(), 42, mergedConfig(), true, - new ActivityWindowInfo())); - } - - @Test - public void testRecycleMoveToDisplayItem() { - testRecycle(() -> MoveToDisplayItem.obtain(mActivityToken, 4, config(), - new ActivityWindowInfo())); - } - - @Test - public void testRecycleNewIntentItem() { - testRecycle(() -> NewIntentItem.obtain(mActivityToken, referrerIntentList(), false)); - } - - @Test - public void testRecyclePauseActivityItemItem() { - testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, true, true)); - } - - @Test - public void testRecycleResumeActivityItem() { - testRecycle(() -> ResumeActivityItem.obtain(mActivityToken, 3, true, false)); - } - - @Test - public void testRecycleStartActivityItem() { - testRecycle(() -> StartActivityItem.obtain(mActivityToken, - new ActivityOptions.SceneTransitionInfo())); - } - - @Test - public void testRecycleStopItem() { - testRecycle(() -> StopActivityItem.obtain(mActivityToken)); - } - - private void testRecycle(@NonNull Supplier<? extends ObjectPoolItem> obtain) { - // Reuse the same object after recycle. - final ObjectPoolItem item = obtain.get(); - item.recycle(); - final ObjectPoolItem item2 = obtain.get(); - - assertNotSame(item, item2); // Different instance. - - // Create new object when the pool is empty. - final ObjectPoolItem item3 = obtain.get(); - - assertNotSame(item, item3); - } -} diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java index c1b9efda4652..24bc547e6782 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -271,7 +271,7 @@ class TestUtils { @NonNull LaunchActivityItem build() { - return LaunchActivityItem.obtain(mActivityToken, mIntent, mIdent, mInfo, + return new LaunchActivityItem(mActivityToken, mIntent, mIdent, mInfo, mCurConfig, mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor, mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions != null ? mActivityOptions.getSceneTransitionInfo() : null, diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java index c2d56b65e95e..76a53d48229e 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java @@ -525,10 +525,6 @@ public class TransactionExecutorTests { } @Override - public void recycle() { - } - - @Override public void writeToParcel(@NonNull Parcel dest, int flags) { } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index dafcf288382a..59d8c8dd3c60 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -74,11 +74,13 @@ public class TransactionParcelTests { @Test public void testConfigurationChange() { // Write to parcel - ConfigurationChangeItem item = ConfigurationChangeItem.obtain(config(), 1 /* deviceId */); + final ConfigurationChangeItem item = + new ConfigurationChangeItem(config(), 1 /* deviceId */); writeAndPrepareForReading(item); // Read from parcel and assert - ConfigurationChangeItem result = ConfigurationChangeItem.CREATOR.createFromParcel(mParcel); + final ConfigurationChangeItem result = + ConfigurationChangeItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -90,7 +92,7 @@ public class TransactionParcelTests { final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000), new Rect(0, 0, 500, 500)); - ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain( + final ActivityConfigurationChangeItem item = new ActivityConfigurationChangeItem( mActivityToken, config(), activityWindowInfo); writeAndPrepareForReading(item); @@ -108,12 +110,12 @@ public class TransactionParcelTests { final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000), new Rect(0, 0, 500, 500)); - MoveToDisplayItem item = MoveToDisplayItem.obtain(mActivityToken, 4 /* targetDisplayId */, - config(), activityWindowInfo); + final MoveToDisplayItem item = new MoveToDisplayItem(mActivityToken, + 4 /* targetDisplayId */, config(), activityWindowInfo); writeAndPrepareForReading(item); // Read from parcel and assert - MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel); + final MoveToDisplayItem result = MoveToDisplayItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -122,11 +124,12 @@ public class TransactionParcelTests { @Test public void testNewIntent() { // Write to parcel - NewIntentItem item = NewIntentItem.obtain(mActivityToken, referrerIntentList(), false); + final NewIntentItem item = + new NewIntentItem(mActivityToken, referrerIntentList(), false /* resume */); writeAndPrepareForReading(item); // Read from parcel and assert - NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel); + final NewIntentItem result = NewIntentItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -135,11 +138,11 @@ public class TransactionParcelTests { @Test public void testActivityResult() { // Write to parcel - ActivityResultItem item = ActivityResultItem.obtain(mActivityToken, resultInfoList()); + final ActivityResultItem item = new ActivityResultItem(mActivityToken, resultInfoList()); writeAndPrepareForReading(item); // Read from parcel and assert - ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel); + final ActivityResultItem result = ActivityResultItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -163,7 +166,7 @@ public class TransactionParcelTests { // Write to parcel final IBinder activityToken = new Binder(); final Intent intent = new Intent("action"); - int ident = 57; + final int ident = 57; final ActivityInfo activityInfo = new ActivityInfo(); activityInfo.flags = 42; activityInfo.setMaxAspectRatio(2.4f); @@ -174,7 +177,7 @@ public class TransactionParcelTests { final Configuration overrideConfig = new Configuration(); overrideConfig.assetsSeq = 5; final String referrer = "referrer"; - int procState = 4; + final int procState = 4; final Bundle bundle = new Bundle(); bundle.putString("key", "value"); bundle.putParcelable("data", new ParcelableData(1)); @@ -216,17 +219,17 @@ public class TransactionParcelTests { @Test public void testRelaunch() { // Write to parcel - Configuration overrideConfig = new Configuration(); + final Configuration overrideConfig = new Configuration(); overrideConfig.assetsSeq = 5; final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo(); activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000), new Rect(0, 0, 500, 500)); - ActivityRelaunchItem item = ActivityRelaunchItem.obtain(mActivityToken, resultInfoList(), + final ActivityRelaunchItem item = new ActivityRelaunchItem(mActivityToken, resultInfoList(), referrerIntentList(), 35, mergedConfig(), true, activityWindowInfo); writeAndPrepareForReading(item); // Read from parcel and assert - ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel); + final ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -235,12 +238,12 @@ public class TransactionParcelTests { @Test public void testPause() { // Write to parcel - PauseActivityItem item = PauseActivityItem.obtain(mActivityToken, true /* finished */, + final PauseActivityItem item = new PauseActivityItem(mActivityToken, true /* finished */, true /* userLeaving */, true /* dontReport */, true /* autoEnteringPip */); writeAndPrepareForReading(item); // Read from parcel and assert - PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel); + final PauseActivityItem result = PauseActivityItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -249,12 +252,12 @@ public class TransactionParcelTests { @Test public void testResume() { // Write to parcel - ResumeActivityItem item = ResumeActivityItem.obtain(mActivityToken, 27 /* procState */, + final ResumeActivityItem item = new ResumeActivityItem(mActivityToken, 27 /* procState */, true /* isForward */, false /* shouldSendCompatFakeFocus */); writeAndPrepareForReading(item); // Read from parcel and assert - ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel); + final ResumeActivityItem result = ResumeActivityItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -263,11 +266,11 @@ public class TransactionParcelTests { @Test public void testStop() { // Write to parcel - StopActivityItem item = StopActivityItem.obtain(mActivityToken); + final StopActivityItem item = new StopActivityItem(mActivityToken); writeAndPrepareForReading(item); // Read from parcel and assert - StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel); + final StopActivityItem result = StopActivityItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -276,12 +279,12 @@ public class TransactionParcelTests { @Test public void testStart() { // Write to parcel - StartActivityItem item = StartActivityItem.obtain(mActivityToken, + final StartActivityItem item = new StartActivityItem(mActivityToken, new ActivityOptions.SceneTransitionInfo()); writeAndPrepareForReading(item); // Read from parcel and assert - StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel); + final StartActivityItem result = StartActivityItem.CREATOR.createFromParcel(mParcel); assertEquals(item.hashCode(), result.hashCode()); assertEquals(item, result); @@ -290,11 +293,12 @@ public class TransactionParcelTests { @Test public void testClientTransaction() { // Write to parcel - NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true); - ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain( + final NewIntentItem callback1 = + new NewIntentItem(mActivityToken, new ArrayList<>(), true /* resume */); + final ActivityConfigurationChangeItem callback2 = new ActivityConfigurationChangeItem( mActivityToken, config(), new ActivityWindowInfo()); - StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken); + final StopActivityItem lifecycleRequest = new StopActivityItem(mActivityToken); final ClientTransaction transaction = new ClientTransaction(); transaction.addTransactionItem(callback1); @@ -304,7 +308,7 @@ public class TransactionParcelTests { writeAndPrepareForReading(transaction); // Read from parcel and assert - ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); + final ClientTransaction result = ClientTransaction.CREATOR.createFromParcel(mParcel); assertEquals(transaction.hashCode(), result.hashCode()); assertEquals(transaction, result); @@ -325,9 +329,7 @@ public class TransactionParcelTests { * android.app.servertransaction.TransactionParcelTests$ParcelableData". */ public static class ParcelableData implements Parcelable { - int mValue; - - ParcelableData() {} + private final int mValue; ParcelableData(int value) { mValue = value; @@ -343,12 +345,10 @@ public class TransactionParcelTests { dest.writeInt(mValue); } - public static final Creator<ParcelableData> CREATOR = new Creator<ParcelableData>() { + public static final Creator<ParcelableData> CREATOR = new Creator<>() { @Override public ParcelableData createFromParcel(Parcel source) { - final ParcelableData data = new ParcelableData(); - data.mValue = source.readInt(); - return data; + return new ParcelableData(source.readInt()); } @Override diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 16bd20a42b2a..75749c7eff09 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -19,6 +19,7 @@ package android.view; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSource.SIDE_BOTTOM; import static android.view.InsetsSource.SIDE_TOP; @@ -54,12 +55,17 @@ import static org.junit.Assert.assertTrue; import android.graphics.Insets; import android.graphics.Rect; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.SparseIntArray; import android.view.WindowInsets.Type; import androidx.test.runner.AndroidJUnit4; +import com.android.window.flags.Flags; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -78,6 +84,9 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class InsetsStateTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int ID_STATUS_BAR = InsetsSource.createId( null /* owner */, 0 /* index */, statusBars()); private static final int ID_NAVIGATION_BAR = InsetsSource.createId( @@ -854,4 +863,19 @@ public class InsetsStateTest { ); } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void testCalculateInsets_forceConsumingCaptionBar() { + mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()) + .setFrame(new Rect(0, 0, 100, 100)) + .setVisible(true) + .setFlags(FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR); + + final WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 1000, 1000), null, false, + SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, ACTIVITY_TYPE_UNDEFINED, + new SparseIntArray()); + + assertTrue(insets.isForceConsumingOpaqueCaptionBar()); + } } diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java index bce3f3e8f2e1..ae3ad36b532c 100644 --- a/core/tests/coretests/src/android/view/ViewGroupTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupTest.java @@ -19,6 +19,7 @@ package android.view; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; @@ -29,11 +30,16 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.graphics.Region; import android.platform.test.annotations.Presubmit; +import android.view.autofill.AutofillId; import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Test basic functions of ViewGroup. @@ -182,6 +188,31 @@ public class ViewGroupTest { assertRegionContainPoint(3 /* x */, r, false /* contain */); // Outside of bounds } + @Test + public void testfindAutofillableViewsByTraversal() { + final Context context = getInstrumentation().getContext(); + final TestView viewGroup = new TestView(context, 200 /* right */); + + // viewA and viewC are autofillable. ViewB isn't. + final TestView viewA = spy(new AutofillableTestView(context, 100 /* right */)); + final TestView viewB = spy(new NonAutofillableTestView(context, 200 /* right */)); + final TestView viewC = spy(new AutofillableTestView(context, 300 /* right */)); + + viewGroup.addView(viewA); + viewGroup.addView(viewB); + viewGroup.addView(viewC); + + List<View> autofillableViews = new ArrayList<>(); + viewGroup.findAutofillableViewsByTraversal(autofillableViews); + + verify(viewA).findAutofillableViewsByTraversal(autofillableViews); + verify(viewB).findAutofillableViewsByTraversal(autofillableViews); + verify(viewC).findAutofillableViewsByTraversal(autofillableViews); + + assertEquals("Size of autofillable views", 2, autofillableViews.size()); + assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC))); + } + private static void getUnobscuredTouchableRegion(Region outRegion, View view) { outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); final ViewParent parent = view.getParent(); @@ -210,4 +241,29 @@ public class ViewGroupTest { // We don't layout this view. } } + + public static class AutofillableTestView extends TestView { + AutofillableTestView(Context context, int right) { + super(context, right); + setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); + // Need to set autofill id in such a way that the view is considered part of activity. + setAutofillId(new AutofillId(LAST_APP_AUTOFILL_ID + 5)); + } + + @Override + public @AutofillType int getAutofillType() { + return AUTOFILL_TYPE_TEXT; + } + } + + public static class NonAutofillableTestView extends TestView { + NonAutofillableTestView(Context context, int right) { + super(context, right); + } + + @Override + public @AutofillType int getAutofillType() { + return AUTOFILL_TYPE_NONE; + } + } } diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java index ba1204b9cc23..c86045d6872a 100644 --- a/core/tests/coretests/src/android/view/WindowInsetsTest.java +++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java @@ -43,14 +43,14 @@ public class WindowInsetsTest { @Test public void systemWindowInsets_afterConsuming_isConsumed() { assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null, - null, false, 0, 0, null, null, null, null, + null, false, 0, false, 0, null, null, null, null, WindowInsets.Type.systemBars(), false, null, null, 0, 0) .consumeSystemWindowInsets().isConsumed()); } @Test public void multiNullConstructor_isConsumed() { - assertTrue(new WindowInsets(null, null, null, false, 0, 0, null, null, null, null, + assertTrue(new WindowInsets(null, null, null, false, 0, false, 0, null, null, null, null, WindowInsets.Type.systemBars(), false, null, null, 0, 0).isConsumed()); } @@ -67,7 +67,7 @@ public class WindowInsetsTest { WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0)); WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0)); WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, 0, - 0, null, null, null, DisplayShape.NONE, systemBars(), + false, 0, null, null, null, DisplayShape.NONE, systemBars(), true /* compatIgnoreVisibility */, null, null, 0, 0); assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets()); } diff --git a/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt b/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt new file mode 100644 index 000000000000..44d10d32606c --- /dev/null +++ b/core/tests/coretests/src/android/widget/RemoteViewsSerializersTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.widget + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Bitmap +import android.graphics.BlendMode +import android.graphics.Color +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Icon +import android.util.proto.ProtoInputStream +import android.util.proto.ProtoOutputStream +import android.widget.RemoteViewsSerializers.createIconFromProto +import android.widget.RemoteViewsSerializers.writeIconToProto +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.frameworks.coretests.R +import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayOutputStream +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class RemoteViewsSerializersTest { + private val context = ApplicationProvider.getApplicationContext<Context>() + + /** + * Based on android.graphics.drawable.IconTest#testParcel + */ + @Test + fun testWriteIconToProto() { + val bitmap = (context.getDrawable(R.drawable.landscape) as BitmapDrawable).bitmap + val bitmapData = ByteArrayOutputStream().let { + bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) + it.toByteArray() + } + + for (icon in listOf( + Icon.createWithBitmap(bitmap), + Icon.createWithAdaptiveBitmap(bitmap), + Icon.createWithData(bitmapData, 0, bitmapData.size), + Icon.createWithResource(context, R.drawable.landscape), + Icon.createWithContentUri("content://com.example.myapp/my_icon"), + Icon.createWithAdaptiveBitmapContentUri("content://com.example.myapp/my_icon"), + )) { + icon.tintList = ColorStateList.valueOf(Color.RED) + icon.tintBlendMode = BlendMode.SRC_OVER + val bytes = ProtoOutputStream().let { + writeIconToProto(it, context.resources, icon) + it.bytes + } + + val copy = ProtoInputStream(bytes).let { + createIconFromProto(it).apply(context.resources) + } + assertThat(copy.type).isEqualTo(icon.type) + assertThat(copy.tintBlendMode).isEqualTo(icon.tintBlendMode) + assertThat(equalColorStateLists(copy.tintList, icon.tintList)).isTrue() + + when (icon.type) { + Icon.TYPE_DATA, Icon.TYPE_URI, Icon.TYPE_URI_ADAPTIVE_BITMAP, + Icon.TYPE_RESOURCE -> { + assertThat(copy.sameAs(icon)).isTrue() + } + + Icon.TYPE_BITMAP, Icon.TYPE_ADAPTIVE_BITMAP -> { + assertThat(copy.bitmap.sameAs(icon.bitmap)).isTrue() + } + } + } + } +} + +fun equalColorStateLists(a: ColorStateList?, b: ColorStateList?): Boolean { + if (a == null && b == null) return true + return a != null && b != null && + a.colors.contentEquals(b.colors) && + a.states.foldIndexed(true) { i, acc, it -> acc && it.contentEquals(b.states[i])} +} diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp index 3ebe150acd24..920ab5914548 100644 --- a/core/tests/vibrator/Android.bp +++ b/core/tests/vibrator/Android.bp @@ -18,6 +18,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.runner", "androidx.test.rules", + "flag-junit", "mockito-target-minus-junit4", "truth", "testng", diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java index e8758754f059..098ade4c1334 100644 --- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java +++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java @@ -20,6 +20,8 @@ import static android.os.VibrationEffect.DEFAULT_AMPLITUDE; import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; import static android.os.VibrationEffect.VibrationParameter.targetFrequency; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -29,6 +31,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertThrows; import android.content.ContentInterface; @@ -38,8 +41,12 @@ import android.content.res.Resources; import android.hardware.vibrator.IVibrator; import android.net.Uri; import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException; +import android.os.vibrator.Flags; +import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; +import android.os.vibrator.VibrationEffectSegment; +import android.platform.test.annotations.RequiresFlagsEnabled; import com.android.internal.R; @@ -284,10 +291,13 @@ public class VibrationEffectTest { } @Test - public void computeLegacyPattern_notPatternPased() { - VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); - - assertNull(effect.computeCreateWaveformOffOnTimingsOrNull()); + public void computeLegacyPattern_notPatternBased() { + assertNull(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK) + .computeCreateWaveformOffOnTimingsOrNull()); + if (Flags.vendorVibrationEffects()) { + assertNull(VibrationEffect.createVendorEffect(createNonEmptyBundle()) + .computeCreateWaveformOffOnTimingsOrNull()); + } } @Test @@ -472,6 +482,18 @@ public class VibrationEffectTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testValidateVendorEffect() { + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putInt("key", 1); + VibrationEffect.createVendorEffect(vendorData).validate(); + + PersistableBundle emptyData = new PersistableBundle(); + assertThrows(IllegalArgumentException.class, + () -> VibrationEffect.createVendorEffect(emptyData).validate()); + } + + @Test public void testValidateWaveform() { VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate(); VibrationEffect.createWaveform(new long[]{10, 10}, new int[] {0, 0}, -1).validate(); @@ -634,16 +656,16 @@ public class VibrationEffectTest { @Test public void testResolveOneShot() { - VibrationEffect.Composed resolved = DEFAULT_ONE_SHOT.resolve(51); - assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude()); + VibrationEffect resolved = DEFAULT_ONE_SHOT.resolve(51); + assertEquals(0.2f, getStepSegment(resolved, 0).getAmplitude()); assertThrows(IllegalArgumentException.class, () -> DEFAULT_ONE_SHOT.resolve(1000)); } @Test public void testResolveWaveform() { - VibrationEffect.Composed resolved = TEST_WAVEFORM.resolve(102); - assertEquals(0.4f, ((StepSegment) resolved.getSegments().get(2)).getAmplitude()); + VibrationEffect resolved = TEST_WAVEFORM.resolve(102); + assertEquals(0.4f, getStepSegment(resolved, 2).getAmplitude()); assertThrows(IllegalArgumentException.class, () -> TEST_WAVEFORM.resolve(1000)); } @@ -655,63 +677,127 @@ public class VibrationEffectTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testResolveVendorEffect() { + VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle()); + assertEquals(effect, effect.resolve(51)); + } + + @Test public void testResolveComposed() { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 1) .compose(); assertEquals(effect, effect.resolve(51)); - VibrationEffect.Composed resolved = VibrationEffect.startComposition() + VibrationEffect resolved = VibrationEffect.startComposition() .addEffect(DEFAULT_ONE_SHOT) .compose() .resolve(51); - assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude()); + assertEquals(0.2f, getStepSegment(resolved, 0).getAmplitude()); } @Test public void testScaleOneShot() { - VibrationEffect.Composed scaledUp = TEST_ONE_SHOT.scale(1.5f); - assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude()); + VibrationEffect scaledUp = TEST_ONE_SHOT.scale(1.5f); + assertTrue(100 / 255f < getStepSegment(scaledUp, 0).getAmplitude()); - VibrationEffect.Composed scaledDown = TEST_ONE_SHOT.scale(0.5f); - assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude()); + VibrationEffect scaledDown = TEST_ONE_SHOT.scale(0.5f); + assertTrue(100 / 255f > getStepSegment(scaledDown, 0).getAmplitude()); } @Test public void testScaleWaveform() { - VibrationEffect.Composed scaledUp = TEST_WAVEFORM.scale(1.5f); - assertEquals(1f, ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude(), 1e-5f); + VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f); + assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f); - VibrationEffect.Composed scaledDown = TEST_WAVEFORM.scale(0.5f); - assertTrue(1f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude()); + VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f); + assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude()); } @Test public void testScalePrebaked() { VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - VibrationEffect.Composed scaledUp = effect.scale(1.5f); + VibrationEffect scaledUp = effect.scale(1.5f); assertEquals(effect, scaledUp); - VibrationEffect.Composed scaledDown = effect.scale(0.5f); + VibrationEffect scaledDown = effect.scale(0.5f); + assertEquals(effect, scaledDown); + } + + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testScaleVendorEffect() { + VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle()); + + VibrationEffect scaledUp = effect.scale(1.5f); + assertEquals(effect, scaledUp); + + VibrationEffect scaledDown = effect.scale(0.5f); assertEquals(effect, scaledDown); } @Test public void testScaleComposed() { - VibrationEffect.Composed effect = - (VibrationEffect.Composed) VibrationEffect.startComposition() + VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1) .addEffect(TEST_ONE_SHOT) .compose(); - VibrationEffect.Composed scaledUp = effect.scale(1.5f); - assertTrue(0.5f < ((PrimitiveSegment) scaledUp.getSegments().get(0)).getScale()); - assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(1)).getAmplitude()); + VibrationEffect scaledUp = effect.scale(1.5f); + assertTrue(0.5f < getPrimitiveSegment(scaledUp, 0).getScale()); + assertTrue(100 / 255f < getStepSegment(scaledUp, 1).getAmplitude()); + + VibrationEffect scaledDown = effect.scale(0.5f); + assertTrue(0.5f > getPrimitiveSegment(scaledDown, 0).getScale()); + assertTrue(100 / 255f > getStepSegment(scaledDown, 1).getAmplitude()); + } + + @Test + public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() { + VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100); + VibrationEffect waveform = VibrationEffect.createWaveform(new long[] { 10, 20 }, 0); + VibrationEffect composition = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + + assertEquals(oneShot, oneShot.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG)); + assertEquals(waveform, + waveform.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG)); + assertEquals(composition, + composition.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG)); + } + + @Test + public void testApplyEffectStrengthToPredefinedEffect() { + VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + + VibrationEffect scaledUp = + effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG); + assertNotEquals(effect, scaledUp); + assertEquals(VibrationEffect.EFFECT_STRENGTH_STRONG, + getPrebakedSegment(scaledUp, 0).getEffectStrength()); + + VibrationEffect scaledDown = + effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT); + assertNotEquals(effect, scaledDown); + assertEquals(VibrationEffect.EFFECT_STRENGTH_LIGHT, + getPrebakedSegment(scaledDown, 0).getEffectStrength()); + } - VibrationEffect.Composed scaledDown = effect.scale(0.5f); - assertTrue(0.5f > ((PrimitiveSegment) scaledDown.getSegments().get(0)).getScale()); - assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude()); + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testApplyEffectStrengthToVendorEffect() { + VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle()); + + VibrationEffect scaledUp = + effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG); + assertNotEquals(effect, scaledUp); + + VibrationEffect scaledDown = + effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT); + assertNotEquals(effect, scaledDown); } private void doTestApplyRepeatingWithNonRepeatingOriginal(@NotNull VibrationEffect original) { @@ -819,6 +905,15 @@ public class VibrationEffectTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testApplyRepeatingIndefinitely_vendorEffect() { + VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle()); + + assertEquals(effect, effect.applyRepeatingIndefinitely(true, 10)); + assertEquals(effect, effect.applyRepeatingIndefinitely(false, 10)); + } + + @Test public void testDuration() { assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration()); assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration()); @@ -832,6 +927,10 @@ public class VibrationEffectTest { new long[]{1, 2, 3}, new int[]{1, 2, 3}, -1).getDuration()); assertEquals(Long.MAX_VALUE, VibrationEffect.createWaveform( new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).getDuration()); + if (Flags.vendorVibrationEffects()) { + assertEquals(-1, + VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration()); + } } @Test @@ -872,6 +971,19 @@ public class VibrationEffectTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testAreVibrationFeaturesSupported_vendorEffects() { + VibratorInfo supportedVibratorInfo = new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS) + .build(); + + assertTrue(VibrationEffect.createVendorEffect(createNonEmptyBundle()) + .areVibrationFeaturesSupported(supportedVibratorInfo)); + assertFalse(VibrationEffect.createVendorEffect(createNonEmptyBundle()) + .areVibrationFeaturesSupported(new VibratorInfo.Builder(/* id= */ 1).build())); + } + + @Test public void testIsHapticFeedbackCandidate_repeatingEffects_notCandidates() { assertFalse(VibrationEffect.createWaveform( new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).isHapticFeedbackCandidate()); @@ -952,6 +1064,13 @@ public class VibrationEffectTest { assertTrue(VibrationEffect.get(VibrationEffect.EFFECT_TICK).isHapticFeedbackCandidate()); } + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testIsHapticFeedbackCandidate_vendorEffects_notCandidates() { + assertFalse(VibrationEffect.createVendorEffect(createNonEmptyBundle()) + .isHapticFeedbackCandidate()); + } + private void assertArrayEq(long[] expected, long[] actual) { assertTrue( String.format("Expected pattern %s, but was %s", @@ -992,4 +1111,35 @@ public class VibrationEffectTest { return context; } + + private StepSegment getStepSegment(VibrationEffect effect, int index) { + VibrationEffectSegment segment = getEffectSegment(effect, index); + assertThat(segment).isInstanceOf(StepSegment.class); + return (StepSegment) segment; + } + + private PrimitiveSegment getPrimitiveSegment(VibrationEffect effect, int index) { + VibrationEffectSegment segment = getEffectSegment(effect, index); + assertThat(segment).isInstanceOf(PrimitiveSegment.class); + return (PrimitiveSegment) segment; + } + + private PrebakedSegment getPrebakedSegment(VibrationEffect effect, int index) { + VibrationEffectSegment segment = getEffectSegment(effect, index); + assertThat(segment).isInstanceOf(PrebakedSegment.class); + return (PrebakedSegment) segment; + } + + private VibrationEffectSegment getEffectSegment(VibrationEffect effect, int index) { + assertThat(effect).isInstanceOf(VibrationEffect.Composed.class); + VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + assertThat(index).isLessThan(composed.getSegments().size()); + return composed.getSegments().get(index); + } + + private PersistableBundle createNonEmptyBundle() { + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt("key", 1); + return bundle; + } } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 5d4139e4be8c..1fe6ca7dc14f 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -505,6 +505,7 @@ applications that come with the platform <permission name="android.permission.RENOUNCE_PERMISSIONS" /> <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" /> + <permission name="android.permission.READ_DROPBOX_DATA" /> <permission name="android.permission.READ_LOGS" /> <permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" /> <permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 544f0f38f48c..3fc409a10781 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -1090,13 +1090,14 @@ class DividerPresenter implements View.OnTouchListener { @NonNull private final SurfaceControl mDividerSurface; @NonNull + private final SurfaceControl mDividerLineSurface; + @NonNull private final WindowlessWindowManager mWindowlessWindowManager; @NonNull private final SurfaceControlViewHost mViewHost; @NonNull private final FrameLayout mDividerLayout; - @NonNull - private final View mDividerLine; + @Nullable private View mDragHandle; @NonNull private final View.OnTouchListener mListener; @@ -1115,7 +1116,10 @@ class DividerPresenter implements View.OnTouchListener { mProperties = properties; mListener = listener; - mDividerSurface = createChildSurface("DividerSurface", true /* visible */); + mDividerSurface = createChildSurface( + mProperties.mDecorSurface, "DividerSurface", true /* visible */); + mDividerLineSurface = createChildSurface( + mDividerSurface, "DividerLineSurface", true /* visible */); mWindowlessWindowManager = new WindowlessWindowManager( mProperties.mConfiguration, mDividerSurface, @@ -1127,7 +1131,6 @@ class DividerPresenter implements View.OnTouchListener { context, displayManager.getDisplay(mProperties.mDisplayId), mWindowlessWindowManager, "DividerContainer"); mDividerLayout = new FrameLayout(context); - mDividerLine = new View(context); update(); } @@ -1220,6 +1223,7 @@ class DividerPresenter implements View.OnTouchListener { dividerSurfacePosition = mDividerPosition; } + // Update the divider surface position relative to the decor surface if (mProperties.mIsVerticalSplit) { t.setPosition(mDividerSurface, dividerSurfacePosition, 0.0f); t.setWindowCrop(mDividerSurface, mDividerSurfaceWidthPx, taskBounds.height()); @@ -1228,10 +1232,24 @@ class DividerPresenter implements View.OnTouchListener { t.setWindowCrop(mDividerSurface, taskBounds.width(), mDividerSurfaceWidthPx); } - // Update divider line position in the surface + // Update divider line surface position relative to the divider surface final int offset = mDividerPosition - dividerSurfacePosition; - mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0); - mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset); + if (mProperties.mIsVerticalSplit) { + t.setPosition(mDividerLineSurface, offset, 0); + t.setWindowCrop(mDividerLineSurface, + mProperties.mDividerWidthPx, taskBounds.height()); + } else { + t.setPosition(mDividerLineSurface, 0, offset); + t.setWindowCrop(mDividerLineSurface, + taskBounds.width(), mProperties.mDividerWidthPx); + } + + // Update divider line surface visibility and color. + // If a container is fully expanded, the divider line is invisible unless dragging. + final boolean isDividerLineVisible = !mProperties.mIsDraggableExpandType || mIsDragging; + t.setVisibility(mDividerLineSurface, isDividerLineVisible); + t.setColor(mDividerLineSurface, colorToFloatArray( + Color.valueOf(mProperties.mDividerAttributes.getDividerColor()))); if (mIsDragging) { updateVeils(t); @@ -1277,21 +1295,6 @@ class DividerPresenter implements View.OnTouchListener { */ private void updateDivider(@NonNull SurfaceControl.Transaction t) { mDividerLayout.removeAllViews(); - mDividerLayout.addView(mDividerLine); - if (mProperties.mIsDraggableExpandType && !mIsDragging) { - // If a container is fully expanded, the divider overlays on the expanded container. - mDividerLine.setBackgroundColor(Color.TRANSPARENT); - } else { - mDividerLine.setBackgroundColor(mProperties.mDividerAttributes.getDividerColor()); - } - final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); - mDividerLine.setLayoutParams( - mProperties.mIsVerticalSplit - ? new FrameLayout.LayoutParams( - mProperties.mDividerWidthPx, taskBounds.height()) - : new FrameLayout.LayoutParams( - taskBounds.width(), mProperties.mDividerWidthPx) - ); if (mProperties.mDividerAttributes.getDividerType() == DividerAttributes.DIVIDER_TYPE_DRAGGABLE) { createVeils(); @@ -1345,10 +1348,11 @@ class DividerPresenter implements View.OnTouchListener { } @NonNull - private SurfaceControl createChildSurface(@NonNull String name, boolean visible) { + private SurfaceControl createChildSurface( + @NonNull SurfaceControl parent, @NonNull String name, boolean visible) { final Rect bounds = mProperties.mConfiguration.windowConfiguration.getBounds(); return new SurfaceControl.Builder() - .setParent(mProperties.mDecorSurface) + .setParent(parent) .setName(name) .setHidden(!visible) .setCallsite("DividerManager.createChildSurface") @@ -1359,10 +1363,12 @@ class DividerPresenter implements View.OnTouchListener { private void createVeils() { if (mPrimaryVeil == null) { - mPrimaryVeil = createChildSurface("DividerPrimaryVeil", false /* visible */); + mPrimaryVeil = createChildSurface( + mProperties.mDecorSurface, "DividerPrimaryVeil", false /* visible */); } if (mSecondaryVeil == null) { - mSecondaryVeil = createChildSurface("DividerSecondaryVeil", false /* visible */); + mSecondaryVeil = createChildSurface( + mProperties.mDecorSurface, "DividerSecondaryVeil", false /* visible */); } } diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 4e7cfb638a12..8669af39747d 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -263,7 +263,7 @@ <!-- Accessibility text for the caption back button [CHAR LIMIT=NONE] --> <string name="back_button_text">Back</string> <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] --> - <string name="handle_text">Handle</string> + <string name="handle_text">App handle</string> <!-- Accessibility text for the handle menu app icon [CHAR LIMIT=NONE] --> <string name="app_icon_text">App Icon</string> <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] --> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index d3fc49bcb766..b1c9a775913a 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -35,7 +35,14 @@ enum class DesktopModeFlags( ) { // All desktop mode related flags will be added here DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true), - WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true); + WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true), + MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true), + THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true), + QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true), + APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true), + TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true), + SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true), + DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true); /** * Determines state of flag based on the actual flag and desktop mode developer option overrides. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index d4d9d003bc0d..fdb45239fa63 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -372,6 +372,7 @@ public class BubbleExpandedView extends LinearLayout { // ==> activity view // ==> manage button bringChildToFront(mManageButton); + setManageClickListener(); applyThemeAttrs(); @@ -502,6 +503,7 @@ public class BubbleExpandedView extends LinearLayout { R.layout.bubble_manage_button, this /* parent */, false /* attach */); addView(mManageButton); mManageButton.setVisibility(visibility); + setManageClickListener(); post(() -> { int touchAreaHeight = getResources().getDimensionPixelSize( @@ -646,9 +648,8 @@ public class BubbleExpandedView extends LinearLayout { } } - // TODO: Could listener be passed when we pass StackView / can we avoid setting this like this - void setManageClickListener(OnClickListener manageClickListener) { - mManageButton.setOnClickListener(manageClickListener); + private void setManageClickListener() { + mManageButton.setOnClickListener(v -> mStackView.onManageBubbleClicked()); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index f93f19d5d1eb..8f8b77b3cf01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1374,7 +1374,6 @@ public class BubbleStackView extends FrameLayout // The menu itself should respect locale direction so the icons are on the correct side. mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE); addView(mManageMenu); - updateManageButtonListener(); } /** @@ -3375,14 +3374,6 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAlpha(0f); mExpandedViewContainer.addView(bev); - postDelayed(() -> { - // Set the Manage button click handler from postDelayed. This appears to resolve - // a race condition with adding the BubbleExpandedView view to the expanded view - // container. Due to the race condition the click handler sometimes is not set up - // correctly and is never called. - updateManageButtonListener(); - }, 0); - if (!mIsExpansionAnimating) { mIsBubbleSwitchAnimating = true; mSurfaceSynchronizer.syncSurfaceAndRun(() -> { @@ -3392,13 +3383,8 @@ public class BubbleStackView extends FrameLayout } } - private void updateManageButtonListener() { - BubbleExpandedView bev = getExpandedView(); - if (mIsExpanded && bev != null) { - bev.setManageClickListener((view) -> { - showManageMenu(true /* show */); - }); - } + void onManageBubbleClicked() { + showManageMenu(true /* show */); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java index d54a6b002e43..c91567d7d8be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java @@ -80,6 +80,7 @@ public class BubbleBarHandleView extends View { outline.setPath(mPath); } }); + setContentDescription(getResources().getString(R.string.handle_text)); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 86cec02ab138..84e32a229f9e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -81,6 +81,7 @@ public class DisplayLayout { private boolean mHasNavigationBar = false; private boolean mHasStatusBar = false; private int mNavBarFrameHeight = 0; + private int mTaskbarFrameHeight = 0; private boolean mAllowSeamlessRotationDespiteNavBarMoving = false; private boolean mNavigationBarCanMove = false; private boolean mReverseDefaultRotation = false; @@ -119,6 +120,7 @@ public class DisplayLayout { && mNavigationBarCanMove == other.mNavigationBarCanMove && mReverseDefaultRotation == other.mReverseDefaultRotation && mNavBarFrameHeight == other.mNavBarFrameHeight + && mTaskbarFrameHeight == other.mTaskbarFrameHeight && Objects.equals(mInsetsState, other.mInsetsState); } @@ -126,7 +128,7 @@ public class DisplayLayout { public int hashCode() { return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi, mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar, - mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving, + mNavBarFrameHeight, mTaskbarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving, mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState); } @@ -176,6 +178,7 @@ public class DisplayLayout { mNavigationBarCanMove = dl.mNavigationBarCanMove; mReverseDefaultRotation = dl.mReverseDefaultRotation; mNavBarFrameHeight = dl.mNavBarFrameHeight; + mTaskbarFrameHeight = dl.mTaskbarFrameHeight; mNonDecorInsets.set(dl.mNonDecorInsets); mStableInsets.set(dl.mStableInsets); mInsetsState.set(dl.mInsetsState, true /* copySources */); @@ -214,7 +217,8 @@ public class DisplayLayout { if (mHasStatusBar) { convertNonDecorInsetsToStableInsets(res, mStableInsets, mCutout, mHasStatusBar); } - mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight); + mNavBarFrameHeight = getNavigationBarFrameHeight(res, /* landscape */ mWidth > mHeight); + mTaskbarFrameHeight = SystemBarUtils.getTaskbarHeight(res); } /** @@ -321,6 +325,17 @@ public class DisplayLayout { outBounds.inset(mStableInsets); } + /** Predicts the calculated stable bounds when in Desktop Mode. */ + public void getStableBoundsForDesktopMode(Rect outBounds) { + getStableBounds(outBounds); + + if (mNavBarFrameHeight != mTaskbarFrameHeight) { + // Currently not in pinned taskbar mode, exclude taskbar insets instead of current + // navigation insets from bounds. + outBounds.bottom = mHeight - mTaskbarFrameHeight; + } + } + /** * Gets navigation bar position for this layout * @return Navigation bar position for this layout. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index cb087a9113ea..f32683db533a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -46,6 +46,7 @@ import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import com.android.wm.shell.compatui.api.CompatUIEvent; import com.android.wm.shell.compatui.impl.CompatUIEvents.CameraControlStateUpdated; import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import java.util.function.Consumer; @@ -94,7 +95,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { mCallback = callback; mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat; if (DESKTOP_WINDOWING_MODE.isEnabled(mContext) - && Flags.enableWindowingDynamicInitialBounds()) { + && DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) { // Don't show the SCM button for freeform tasks mHasSizeCompat &= !taskInfo.isFreeform(); } @@ -154,7 +155,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { final int prevCameraCompatControlState = mCameraCompatControlState; mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat; if (DESKTOP_WINDOWING_MODE.isEnabled(mContext) - && Flags.enableWindowingDynamicInitialBounds()) { + && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) { // Don't show the SCM button for freeform tasks mHasSizeCompat &= !taskInfo.isFreeform(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 4b548cbe77cc..2dc6382d494d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -946,10 +946,11 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static TaskStackTransitionObserver provideTaskStackTransitionObserver( + Context context, Lazy<Transitions> transitions, ShellInit shellInit ) { - return new TaskStackTransitionObserver(transitions, shellInit); + return new TaskStackTransitionObserver(context, transitions, shellInit); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 886609a0c323..2ef045d9fb89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -80,6 +80,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.recents.RecentTasksController import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity @@ -348,7 +349,7 @@ class DesktopTasksController( wct: WindowContainerTransaction = WindowContainerTransaction(), transitionSource: DesktopModeTransitionSource, ) { - if (Flags.enableDesktopWindowingModalsPolicy() + if (DesktopModeFlags.MODALS_POLICY.isEnabled(context) && isTopActivityExemptFromDesktopWindowing(context, task)) { ProtoLog.w( WM_SHELL_DESKTOP_MODE, @@ -663,7 +664,7 @@ class DesktopTasksController( if (taskBoundsBeforeMaximize != null) { destinationBounds.set(taskBoundsBeforeMaximize) } else { - if (Flags.enableWindowingDynamicInitialBounds()) { + if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) { destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo)) } else { destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout)) @@ -971,7 +972,7 @@ class DesktopTasksController( } private fun isIncompatibleTask(task: TaskInfo) = - Flags.enableDesktopWindowingModalsPolicy() + DesktopModeFlags.MODALS_POLICY.isEnabled(context) && isTopActivityExemptFromDesktopWindowing(context, task) private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean { @@ -1361,7 +1362,7 @@ class DesktopTasksController( // Start a new jank interaction for the drag release to desktop window animation. interactionJankMonitor.begin(taskSurface, context, CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop") - if (Flags.enableWindowingDynamicInitialBounds()) { + if (DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) { finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo)) } else { finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout)) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index d001b2c09f85..54f908b395e7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -20,7 +20,6 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps; -import static com.android.window.flags.Flags.enableTaskStackObserverInShell; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import android.app.ActivityManager; @@ -55,6 +54,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.shared.annotations.ShellMainThread; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -351,7 +351,7 @@ public class RecentTasksController implements TaskStackListenerCallback, private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { if (mListener == null - || !enableTaskStackObserverInShell() + || !DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(mContext) || taskInfo.realActivity == null) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt index 8ee72b499e5a..3a0bdb94e1a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt @@ -18,13 +18,14 @@ package com.android.wm.shell.recents import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.content.Context import android.os.IBinder import android.util.ArrayMap import android.view.SurfaceControl import android.view.WindowManager import android.window.TransitionInfo -import com.android.window.flags.Flags.enableTaskStackObserverInShell import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import dagger.Lazy @@ -37,6 +38,7 @@ import java.util.concurrent.Executor * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it */ class TaskStackTransitionObserver( + private val context: Context, private val transitions: Lazy<Transitions>, shellInit: ShellInit ) : Transitions.TransitionObserver { @@ -62,7 +64,7 @@ class TaskStackTransitionObserver( startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction ) { - if (enableTaskStackObserverInShell()) { + if (DesktopModeFlags.TASK_STACK_OBSERVER_IN_SHELL.isEnabled(context)) { val taskInfoList = mutableListOf<RunningTaskInfo>() val transitionTypeList = mutableListOf<Int>() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index a77a76c4188b..7f1f365640c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -101,6 +101,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreen.StageType; @@ -1124,7 +1125,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && taskInfo.isFocused) { return false; } - if (Flags.enableDesktopWindowingModalsPolicy() + if (DesktopModeFlags.MODALS_POLICY.isEnabled(mContext) && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) { return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index a1cc6507c280..be16207dc2d5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -19,6 +19,8 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.windowingModeToString; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -75,6 +77,7 @@ 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.shared.annotations.ShellBackgroundThread; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.common.OnTaskActionClickListener; @@ -592,6 +595,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // through to the windows below so that the app can respond to input events on // their custom content. relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } else { + if (Flags.enableCaptionCompatInsetForceConsumption()) { + // Force-consume the caption bar insets when the app tries to hide the caption. + // This improves app compatibility of immersive apps. + relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING; + } + } + if (Flags.enableCaptionCompatInsetForceConsumptionAlways()) { + // Always force-consume the caption bar insets for maximum app compatibility, + // including non-immersive apps that just don't handle caption insets properly. + relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; } // Report occluding elements as bounding rects to the insets system so that apps can // draw in the empty space in the center: @@ -630,7 +644,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // TODO(b/301119301): consider moving the config data needed for diffs to relayout params // instead of using a whole Configuration as a parameter. final Configuration windowDecorConfig = new Configuration(); - if (Flags.enableAppHeaderWithTaskDensity() && isAppHeader) { + if (DesktopModeFlags.APP_HEADER_WITH_TASK_DENSITY.isEnabled(context) && isAppHeader) { // Should match the density of the task. The task may have had its density overridden // to be different that SysUI's. windowDecorConfig.setTo(taskInfo.configuration); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java index 2fd3eaa37ec3..0f2de700d3ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java @@ -30,9 +30,9 @@ import android.view.SurfaceControl; import androidx.annotation.NonNull; -import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; /** @@ -245,7 +245,7 @@ public class DragPositioningCallbackUtility { private static boolean isSizeConstraintForDesktopModeEnabled(Context context) { return DesktopModeStatus.canEnterDesktopMode(context) - && Flags.enableDesktopWindowingSizeConstraints(); + && DesktopModeFlags.SIZE_CONSTRAINTS.isEnabled(context); } interface DragStartListener { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt index 4f049015af99..4faed01e9e82 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt @@ -31,8 +31,8 @@ import android.widget.ProgressBar import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart import androidx.core.content.ContextCompat -import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350 private const val MAX_DRAWABLE_ALPHA = 255 @@ -108,7 +108,7 @@ class MaximizeButtonView( baseForegroundColor: Int? = null, rippleDrawable: RippleDrawable? = null ) { - if (Flags.enableThemedAppHeaders()) { + if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) { requireNotNull(iconForegroundColor) { "Icon foreground color must be non-null" } requireNotNull(baseForegroundColor) { "Base foreground color must be non-null" } requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index a691f59a2155..5f00aa28d666 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -19,7 +19,6 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; -import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowInsets.Type.statusBars; @@ -54,7 +53,6 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; -import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -375,7 +373,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } final WindowDecorationInsets newInsets = new WindowDecorationInsets( - mTaskInfo.token, mOwner, captionInsetsRect, boundingRects); + mTaskInfo.token, mOwner, captionInsetsRect, boundingRects, + params.mInsetSourceFlags); if (!newInsets.equals(mWindowDecorationInsets)) { // Add or update this caption as an insets source. mWindowDecorationInsets = newInsets; @@ -660,7 +659,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId); final Rect captionInsets = new Rect(0, 0, 0, captionHeight); final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token, - mOwner, captionInsets, null /* boundingRets */); + mOwner, captionInsets, null /* boundingRets */, 0 /* flags */); if (!newInsets.equals(mWindowDecorationInsets)) { mWindowDecorationInsets = newInsets; mWindowDecorationInsets.addOrUpdate(wct); @@ -674,6 +673,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionWidthId; final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); int mInputFeatures; + @InsetsSource.Flags int mInsetSourceFlags; int mShadowRadiusId; int mCornerRadius; @@ -689,6 +689,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); mInputFeatures = 0; + mInsetSourceFlags = 0; mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -753,20 +754,20 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private final Binder mOwner; private final Rect mFrame; private final Rect[] mBoundingRects; + private final @InsetsSource.Flags int mFlags; private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame, - Rect[] boundingRects) { + Rect[] boundingRects, @InsetsSource.Flags int flags) { mToken = token; mOwner = owner; mFrame = frame; mBoundingRects = boundingRects; + mFlags = flags; } void addOrUpdate(WindowContainerTransaction wct) { - final @InsetsSource.Flags int captionSourceFlags = - Flags.enableCaptionCompatInsetForceConsumption() ? FLAG_FORCE_CONSUMING : 0; wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects, - captionSourceFlags); + mFlags); wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame, mBoundingRects, 0 /* flags */); } @@ -782,12 +783,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false; return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner, that.mOwner) && Objects.equals(mFrame, that.mFrame) - && Objects.deepEquals(mBoundingRects, that.mBoundingRects); + && Objects.deepEquals(mBoundingRects, that.mBoundingRects) + && mFlags == that.mFlags; } @Override public int hashCode() { - return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects)); + return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags); } } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index b704d9c001c8..17b3dea5db75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -42,8 +42,8 @@ import com.android.internal.R.attr.materialColorSecondaryContainer import com.android.internal.R.attr.materialColorSurfaceContainerHigh import com.android.internal.R.attr.materialColorSurfaceContainerLow import com.android.internal.R.attr.materialColorSurfaceDim -import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.shared.desktopmode.DesktopModeFlags import com.android.wm.shell.windowdecor.MaximizeButtonView import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.OPACITY_100 @@ -144,7 +144,7 @@ internal class AppHeaderViewHolder( height: Int, isCaptionVisible: Boolean ) { - if (Flags.enableThemedAppHeaders()) { + if (DesktopModeFlags.THEMED_APP_HEADERS.isEnabled(context)) { bindDataWithThemedHeaders(taskInfo) } else { bindDataLegacy(taskInfo) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt index 0e5efa650cc4..bc9b44e59d89 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.recents import android.app.ActivityManager import android.app.WindowConfiguration +import android.content.Context import android.os.IBinder import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule @@ -59,6 +60,7 @@ class TaskStackTransitionObserverTest { @JvmField @Rule val setFlagsRule = SetFlagsRule() + @Mock private lateinit var context: Context @Mock private lateinit var shellInit: ShellInit @Mock lateinit var testExecutor: ShellExecutor @Mock private lateinit var transitionsLazy: Lazy<Transitions> @@ -72,7 +74,7 @@ class TaskStackTransitionObserverTest { MockitoAnnotations.initMocks(this) shellInit = Mockito.spy(ShellInit(testExecutor)) whenever(transitionsLazy.get()).thenReturn(transitions) - transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit) + transitionObserver = TaskStackTransitionObserver(context, transitionsLazy, shellInit) if (Transitions.ENABLE_SHELL_TRANSITIONS) { val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) verify(shellInit) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 4b069f933d9f..9c3016eed25d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -20,6 +20,8 @@ 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; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; @@ -411,6 +413,80 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) + public void updateRelayoutParams_defaultHeader_addsForceConsumingFlag() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) + public void updateRelayoutParams_customHeader_noForceConsumptionFlag() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void updateRelayoutParams_header_addsForceConsumingCaptionBar() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat( + (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) + .isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS) + public void updateRelayoutParams_handle_skipsForceConsumingCaptionBar() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat( + (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0) + .isTrue(); + } + @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR) public void relayout_fullscreenTask_appliesTransactionImmediately() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 2d1bf14ffbb3..ca6e03c45e7e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.InsetsSource.FLAG_FORCE_CONSUMING; +import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowInsets.Type.statusBars; @@ -56,7 +57,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; @@ -75,7 +75,6 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.StaticMockitoSession; -import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; @@ -781,8 +780,7 @@ public class WindowDecorationTests extends ShellTestCase { } @Test - @EnableFlags(Flags.FLAG_ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION) - public void testRelayout_captionInsetForceConsume() { + public void testRelayout_captionInsetSourceFlags() { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController) .getDisplay(Display.DEFAULT_DISPLAY); @@ -794,11 +792,14 @@ public class WindowDecorationTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = builder.setToken(token).setBounds(new Rect(0, 0, 1000, 1000)).build(); final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); + mRelayoutParams.mInsetSourceFlags = + FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR; windowDecor.relayout(taskInfo); - // Caption inset source should be force-consuming. + // Caption inset source should add params' flags. verify(mMockWindowContainerTransaction).addInsetsSource(eq(token), any(), - eq(0) /* index */, eq(captionBar()), any(), any(), eq(FLAG_FORCE_CONSUMING)); + eq(0) /* index */, eq(captionBar()), any(), any(), + eq(FLAG_FORCE_CONSUMING | FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR)); } @Test diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index d4157008ca46..010c4e8dfb3a 100644 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -33,9 +33,6 @@ #define DEBUG_PARCEL 0 static jclass gBitmap_class; -static jfieldID gBitmap_nativePtr; -static jmethodID gBitmap_constructorMethodID; -static jmethodID gBitmap_reinitMethodID; namespace android { @@ -183,6 +180,9 @@ static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied) { + static jmethodID gBitmap_reinitMethodID = + GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); + // The caller needs to have already set the alpha type properly, so the // native SkBitmap stays in sync with the Java Bitmap. assert_premultiplied(info, isPremultiplied); @@ -194,6 +194,10 @@ void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, jobject createBitmap(JNIEnv* env, Bitmap* bitmap, int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density) { + static jmethodID gBitmap_constructorMethodID = + GetMethodIDOrDie(env, gBitmap_class, + "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); + bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable; bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied; // The caller needs to have already set the alpha type properly, so the @@ -232,11 +236,17 @@ Bitmap& toBitmap(jlong bitmapHandle) { using namespace android; using namespace android::bitmap; +static inline jlong getNativePtr(JNIEnv* env, jobject bitmap) { + static jfieldID gBitmap_nativePtr = + GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); + return env->GetLongField(bitmap, gBitmap_nativePtr); +} + Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { SkASSERT(env); SkASSERT(bitmap); SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + jlong bitmapHandle = getNativePtr(env, bitmap); LocalScopedBitmap localBitmap(bitmapHandle); return localBitmap.valid() ? &localBitmap->bitmap() : nullptr; } @@ -246,7 +256,7 @@ SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* ou SkASSERT(env); SkASSERT(bitmap); SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); - jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr); + jlong bitmapHandle = getNativePtr(env, bitmap); LocalScopedBitmap localBitmap(bitmapHandle); if (outRowBytes) { *outRowBytes = localBitmap->rowBytes(); @@ -1269,9 +1279,6 @@ static const JNINativeMethod gBitmapMethods[] = { int register_android_graphics_Bitmap(JNIEnv* env) { gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); - gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); - gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); - gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); uirenderer::HardwareBufferHelpers::init(); return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index 6744c6c0b9ec..aebc4db37898 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -162,8 +162,8 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor, jlong length, jboolean preferAnimation, jobject source) { -#ifndef __ANDROID__ // LayoutLib for Windows does not support F_DUPFD_CLOEXEC - return throw_exception(env, kSourceException, "Only supported on Android", nullptr, source); +#ifdef _WIN32 // LayoutLib for Windows does not support F_DUPFD_CLOEXEC + return throw_exception(env, kSourceException, "Not supported on Windows", nullptr, source); #else int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 8acaf3be5152..e575daeb8d29 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -5263,6 +5263,8 @@ final public class MediaCodec { * main thread.) */ public void setCallback(@Nullable /* MediaCodec. */ Callback cb, @Nullable Handler handler) { + boolean setCallbackStallFlag = + GetFlag(() -> android.media.codec.Flags.setCallbackStall()); if (cb != null) { synchronized (mListenerLock) { EventHandler newHandler = getEventHandlerOn(handler, mCallbackHandler); @@ -5270,7 +5272,7 @@ final public class MediaCodec { // even if we were to extend this to be callable dynamically, it must // be called when codec is flushed, so no messages are pending. if (newHandler != mCallbackHandler) { - if (android.media.codec.Flags.setCallbackStall()) { + if (setCallbackStallFlag) { logAndRun( "[new handler] removeMessages(SET_CALLBACK)", () -> { @@ -5289,7 +5291,7 @@ final public class MediaCodec { } } } else if (mCallbackHandler != null) { - if (android.media.codec.Flags.setCallbackStall()) { + if (setCallbackStallFlag) { logAndRun( "[null handler] removeMessages(SET_CALLBACK)", () -> { diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 0667bfda7596..1930c3d8b9b7 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -107,7 +107,8 @@ public final class MediaRouter2 { * #SCANNING_STATE_WHILE_INTERACTIVE}. * * <p>Routers requesting unrestricted scanning must hold {@link - * Manifest.permission#MEDIA_ROUTING_CONTROL}. + * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link + * Manifest.permission#MEDIA_CONTENT_CONTROL}. * * @hide */ @@ -522,11 +523,16 @@ public final class MediaRouter2 { * * <p>{@code scanRequest} specifies relevant scanning options, like whether the system should * scan with the screen off. Screen off scanning requires {@link - * Manifest.permission#MEDIA_ROUTING_CONTROL} + * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link + * Manifest.permission#MEDIA_CONTENT_CONTROL}. * * <p>Proxy routers use the registered {@link RouteDiscoveryPreference} of their target routers. * * @return A unique {@link ScanToken} that identifies the scan request. + * @throws SecurityException If a {@link ScanRequest} with {@link + * ScanRequest.Builder#setScreenOffScan} true is passed, while not holding {@link + * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link + * Manifest.permission#MEDIA_CONTENT_CONTROL}. */ @FlaggedApi(FLAG_ENABLE_SCREEN_OFF_SCANNING) @NonNull @@ -1745,8 +1751,9 @@ public final class MediaRouter2 { /** * Sets whether the app is requesting to scan even while the screen is off, bypassing - * default scanning restrictions. Only companion apps holding {@link - * Manifest.permission#MEDIA_ROUTING_CONTROL} should set this to {@code true}. + * default scanning restrictions. Only apps holding {@link + * Manifest.permission#MEDIA_ROUTING_CONTROL} or {@link + * Manifest.permission#MEDIA_CONTENT_CONTROL} should set this to {@code true}. * * @see #requestScan(ScanRequest) */ diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 91c4f118658a..7c41f968a03b 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -134,3 +134,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_full_scan_with_media_content_control" + namespace: "media_better_together" + description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground." + bug: "352401364" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index f64233fb9c79..6776f611559c 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -24,7 +24,6 @@ #include <utils/String8.h> #include <utils/Thread.h> -#include <gui/IProducerListener.h> #include <gui/Surface.h> #include <ui/PublicFormat.h> #include <android_runtime/AndroidRuntime.h> @@ -65,15 +64,18 @@ static struct { // ---------------------------------------------------------------------------- -class JNIImageWriterContext : public BnProducerListener { +class JNIImageWriterContext : public SurfaceListener { public: JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz); virtual ~JNIImageWriterContext(); - // Implementation of IProducerListener, used to notify the ImageWriter that the consumer + // Implementation of SurfaceListener, used to notify the ImageWriter that the consumer // has returned a buffer and it is ready for ImageWriter to dequeue. virtual void onBufferReleased(); + virtual bool needsReleaseNotify() override { return true; }; + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& /*buffers*/) override {}; + virtual void onBufferDetached(int /*slot*/) override {}; void setProducer(const sp<Surface>& producer) { mProducer = producer; } Surface* getProducer() { return mProducer.get(); } diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 403e219f3467..83a986b1aa76 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -112,3 +112,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "settings_catalyst" + namespace: "android_settings" + description: "Settings catalyst project migration" + bug: "323791114" +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java index 054c84952fae..c48694cecb4c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/EnableZenModeDialog.java @@ -159,9 +159,9 @@ public class EnableZenModeDialog { }); if (mCancelIsNeutral) { - builder.setNeutralButton(R.string.cancel, null); + builder.setNeutralButton(android.R.string.cancel, null); } else { - builder.setNegativeButton(R.string.cancel, null); + builder.setNegativeButton(android.R.string.cancel, null); } View contentView = getContentView(); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 04d30ed6f001..0b5187c44821 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -65,6 +65,7 @@ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" /> <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" /> + <uses-permission android:name="android.permission.READ_DROPBOX_DATA" /> <uses-permission android:name="android.permission.READ_LOGS" /> <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" /> <uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" /> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 02e042327d66..eb9d0ab9d42c 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1243,4 +1243,13 @@ flag { metadata { purpose: PURPOSE_BUGFIX } -}
\ No newline at end of file +} +flag { + name: "classic_flags_multi_user" + namespace: "systemui" + description: "Make the classic feature flag loading multi user aware." + bug: "345443431" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt index f73b6cd156f3..9fd30b499595 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt @@ -33,7 +33,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow object Bouncer { object Elements { @@ -56,7 +56,7 @@ constructor( ) : ComposableScene { override val key = Scenes.Bouncer - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = viewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 68e968fdfb4e..1c6b4cf32369 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -791,7 +791,7 @@ private fun ToolbarButton( onClick = onClick, colors = ButtonDefaults.outlinedButtonColors( - contentColor = colors.primary, + contentColor = colors.onPrimaryContainer, ), border = BorderStroke(width = 2.0.dp, color = colors.primary), contentPadding = Dimensions.ButtonPadding, @@ -855,7 +855,7 @@ private fun CommunalContent( /** Creates an empty card used to highlight a particular spot on the grid. */ @Composable fun HighlightedItem(modifier: Modifier = Modifier, alpha: Float = 1.0f) { - val brush = SolidColor(LocalAndroidColorScheme.current.primaryFixed) + val brush = SolidColor(LocalAndroidColorScheme.current.primary) Box( modifier = // drawBehind lets us draw outside the bounds of the widgets so that we don't need to @@ -993,6 +993,11 @@ private fun WidgetContent( modifier = modifier .then(selectableModifier) + .thenIf(!viewModel.isEditMode && !model.inQuietMode) { + Modifier.pointerInput(Unit) { + observeTaps { viewModel.onTapWidget(model.componentName, model.priority) } + } + } .thenIf(!viewModel.isEditMode && model.inQuietMode) { Modifier.pointerInput(Unit) { // consume tap to prevent the child view from triggering interactions with diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt index 94018bbdbd22..e0fc340e14f4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt @@ -30,8 +30,8 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import com.android.systemui.statusbar.phone.SystemUIDialogFactory import javax.inject.Inject +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** The communal scene shows glanceable hub when the device is locked and docked. */ @@ -45,7 +45,7 @@ constructor( ) : ComposableScene { override val key = Scenes.Communal - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = MutableStateFlow<Map<UserAction, UserActionResult>>( mapOf( Swipe(SwipeDirection.Right) to UserActionResult(Scenes.Lockscreen), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt index c241f9ca7253..b077e183e8ed 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt @@ -30,7 +30,7 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.ComposableScene import dagger.Lazy import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow /** The lock screen scene shows when the device is locked. */ @SysUISingleton @@ -42,7 +42,7 @@ constructor( ) : ComposableScene { override val key = Scenes.Lockscreen - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = viewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt index 7400711e36df..c01039d52d78 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt @@ -94,7 +94,7 @@ constructor( with(topAreaSection) { DefaultClockLayout( modifier = - Modifier.graphicsLayer { + Modifier.fillMaxWidth(0.5f).graphicsLayer { translationX = unfoldTranslations.start } ) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt index db98bc8fff9c..7159def8d60a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import dagger.Lazy import java.util.Optional import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow @SysUISingleton class NotificationsShadeScene @@ -64,7 +64,7 @@ constructor( override val key = Scenes.NotificationsShade - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = sceneViewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 3cf8e70d458f..2800eee661c6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -69,6 +69,8 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.modifiers.thenIf @@ -79,7 +81,6 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost @@ -109,16 +110,13 @@ import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlin.math.roundToInt -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.Flow /** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */ @SysUISingleton class QuickSettingsScene @Inject constructor( - @Application private val applicationScope: CoroutineScope, private val shadeSession: SaveableSession, private val notificationStackScrollView: Lazy<NotificationScrollView>, private val viewModel: QuickSettingsSceneViewModel, @@ -131,12 +129,8 @@ constructor( ) : ComposableScene { override val key = Scenes.QuickSettings - override val destinationScenes = - viewModel.destinationScenes.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = emptyMap(), - ) + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + viewModel.destinationScenes @Composable override fun SceneScope.Content( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt index 422c9f618fa1..f6d1283e1f29 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt @@ -60,7 +60,7 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import dagger.Lazy import java.util.Optional import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow @SysUISingleton class QuickSettingsShadeScene @@ -76,7 +76,7 @@ constructor( override val key = Scenes.QuickSettingsShade - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = viewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index a44041a6ebf7..a9ddf846c5c3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScr import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import dagger.Lazy import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow /** * "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any @@ -52,7 +52,7 @@ constructor( ) : ComposableScene { override val key = Scenes.Gone - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = viewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 9c9e6c69de1c..d5874d1a7d3f 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -14,8 +14,6 @@ * limitations under the License. */ -@file:OptIn(ExperimentalComposeUiApi::class) - package com.android.systemui.scene.ui.composable import androidx.compose.foundation.layout.Box @@ -23,14 +21,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayout @@ -40,6 +37,7 @@ import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel +import kotlinx.coroutines.flow.collectLatest /** * Renders a container of a collection of "scenes" that the user can switch between using certain @@ -62,19 +60,20 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel fun SceneContainer( viewModel: SceneContainerViewModel, sceneByKey: Map<SceneKey, ComposableScene>, + initialSceneKey: SceneKey, dataSourceDelegator: SceneDataSourceDelegator, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() - val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle() val state: MutableSceneTransitionLayoutState = remember { MutableSceneTransitionLayoutState( - initialScene = currentSceneKey, + initialScene = initialSceneKey, canChangeScene = { toScene -> viewModel.canChangeScene(toScene) }, transitions = SceneContainerTransitions, enableInterruptions = false, ) } + val currentSceneKey = state.transitionState.currentScene DisposableEffect(state) { val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope) @@ -87,19 +86,32 @@ fun SceneContainer( onDispose { viewModel.setTransitionState(null) } } - val userActionsBySceneKey: Map<SceneKey, Map<UserAction, UserActionResult>> = - sceneByKey.values.associate { scene -> - val userActions by scene.destinationScenes.collectAsStateWithLifecycle(emptyMap()) - val resolvedUserActions = viewModel.resolveSceneFamilies(userActions) - scene.key to resolvedUserActions + val userActionsBySceneKey: MutableMap<SceneKey, Map<UserAction, UserActionResult>> = remember { + mutableStateMapOf() + } + LaunchedEffect(currentSceneKey) { + try { + sceneByKey[currentSceneKey]?.destinationScenes?.collectLatest { userActions -> + userActionsBySceneKey[currentSceneKey] = viewModel.resolveSceneFamilies(userActions) + } + } finally { + userActionsBySceneKey[currentSceneKey] = emptyMap() } + } Box( modifier = Modifier.fillMaxSize(), ) { SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) { sceneByKey.forEach { (sceneKey, composableScene) -> - scene(key = sceneKey, userActions = checkNotNull(userActionsBySceneKey[sceneKey])) { + scene( + key = sceneKey, + userActions = userActionsBySceneKey.getOrDefault(sceneKey, emptyMap()) + ) { + // Activate the scene. + LaunchedEffect(composableScene) { composableScene.activate() } + + // Render the scene. with(composableScene) { this@scene.Content( modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 09414a9a0d0f..21e3431163ec 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -109,7 +109,7 @@ import dagger.Lazy import javax.inject.Inject import javax.inject.Named import kotlin.math.roundToInt -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow object Shade { object Elements { @@ -153,7 +153,11 @@ constructor( override val key = Scenes.Shade - override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + override suspend fun activate() { + viewModel.activate() + } + + override val destinationScenes: Flow<Map<UserAction, UserActionResult>> = viewModel.destinationScenes @Composable diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 5b328b8cdd9c..fb13b57176c6 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -712,9 +712,7 @@ private class SwipeTransition( val hasReachedTargetScene = (targetScene == toScene && progress >= 1f) || (targetScene == fromScene && progress <= 0f) - val skipAnimation = - hasReachedTargetScene && - currentOverscrollSpec?.transformationSpec?.transformations?.isEmpty() == true + val skipAnimation = hasReachedTargetScene && !canOverscroll() return startOffsetAnimation { val animatable = Animatable(dragOffset, OffsetVisibilityThreshold) @@ -767,12 +765,7 @@ private class SwipeTransition( // Immediately stop this transition if we are bouncing on a // scene that does not bounce. - val overscrollSpec = currentOverscrollSpec - if ( - overscrollSpec != null && - overscrollSpec.transformationSpec.transformations - .isEmpty() - ) { + if (!canOverscroll()) { snapToScene(targetScene) } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index 2b78b5adaa62..324e7bd040f3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -115,18 +115,6 @@ private data class MultiPointerDraggableElement( } } -private val TRAVERSE_KEY = Any() - -/** Find the nearest [PointersInfoOwner] ancestor or throw. */ -internal fun DelegatableNode.requireAncestorPointersInfoOwner(): PointersInfoOwner { - val ancestorNode = - checkNotNull(findNearestAncestor(TRAVERSE_KEY)) { - "This should never happen! Couldn't find a MultiPointerDraggableNode. " + - "Are we inside an SceneTransitionLayout?" - } - return ancestorNode as PointersInfoOwner -} - internal class MultiPointerDraggableNode( orientation: Orientation, enabled: () -> Boolean, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 08e8e7250e0c..2a739d78ccc1 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -233,6 +233,12 @@ sealed interface TransitionState { } } + /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */ + fun canOverscroll(): Boolean { + val overscrollSpec = currentOverscrollSpec ?: return true + return overscrollSpec.transformationSpec.transformations.isNotEmpty() + } + /** * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden * jump of values when this transitions interrupts another one. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index bbd2f6b0174a..9ccf99b301b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dock.dockManager import com.android.systemui.dock.fakeDockManager @@ -106,6 +107,27 @@ class CommunalSceneStartableTest : SysuiTestCase() { @Test @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) + fun keyguardGoesAway_whenLaunchingEditMode_doNotForceBlankScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalSceneInteractor.currentScene) + + communalSceneInteractor.changeScene(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) + + communalSceneInteractor.setEditModeState(EditModeState.STARTING) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.PRIMARY_BOUNCER, + to = KeyguardState.GONE, + testScope = this + ) + + assertThat(scene).isEqualTo(CommunalScenes.Communal) + } + } + + @Test + @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) fun keyguardGoesAway_whenLaunchingWidget_doNotForceBlankScene() = with(kosmos) { testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt index def8698b7e4b..28ad269776f9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalLoggerStartableTest.kt @@ -22,10 +22,13 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase -import com.android.systemui.communal.domain.interactor.CommunalInteractor -import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -53,18 +56,21 @@ class CommunalLoggerStartableTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope - private lateinit var communalInteractor: CommunalInteractor + private lateinit var communalSceneInteractor: CommunalSceneInteractor + private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var underTest: CommunalLoggerStartable @Before fun setUp() { MockitoAnnotations.initMocks(this) - communalInteractor = kosmos.communalInteractor + communalSceneInteractor = kosmos.communalSceneInteractor + keyguardRepository = kosmos.fakeKeyguardRepository underTest = CommunalLoggerStartable( testScope.backgroundScope, - communalInteractor, + communalSceneInteractor, + kosmos.keyguardInteractor, uiEventLogger, ) underTest.start() @@ -73,10 +79,13 @@ class CommunalLoggerStartableTest : SysuiTestCase() { @Test fun transitionStateLogging_enterCommunalHub() = testScope.runTest { + // Not dreaming + keyguardRepository.setDreamingWithOverlay(false) + // Transition state is default (non-communal) val transitionState = MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) - communalInteractor.setTransitionState(transitionState) + communalSceneInteractor.setTransitionState(transitionState) runCurrent() // Verify nothing is logged from the default state @@ -99,12 +108,15 @@ class CommunalLoggerStartableTest : SysuiTestCase() { } @Test - fun transitionStateLogging_enterCommunalHub_canceled() = + fun transitionStateLogging_cancelEnteringCommunalHub() = testScope.runTest { + // Not dreaming + keyguardRepository.setDreamingWithOverlay(false) + // Transition state is default (non-communal) val transitionState = MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) - communalInteractor.setTransitionState(transitionState) + communalSceneInteractor.setTransitionState(transitionState) runCurrent() // Verify nothing is logged from the default state @@ -132,10 +144,13 @@ class CommunalLoggerStartableTest : SysuiTestCase() { @Test fun transitionStateLogging_exitCommunalHub() = testScope.runTest { + // Not dreaming + keyguardRepository.setDreamingWithOverlay(false) + // Transition state is communal val transitionState = MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) - communalInteractor.setTransitionState(transitionState) + communalSceneInteractor.setTransitionState(transitionState) runCurrent() // Verify SHOWN is logged when it's the default state @@ -158,12 +173,15 @@ class CommunalLoggerStartableTest : SysuiTestCase() { } @Test - fun transitionStateLogging_exitCommunalHub_canceled() = + fun transitionStateLogging_cancelExitingCommunalHub() = testScope.runTest { + // Not dreaming + keyguardRepository.setDreamingWithOverlay(false) + // Transition state is communal val transitionState = MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) - communalInteractor.setTransitionState(transitionState) + communalSceneInteractor.setTransitionState(transitionState) runCurrent() // Clear the initial SHOWN event from the logger @@ -188,6 +206,136 @@ class CommunalLoggerStartableTest : SysuiTestCase() { verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) } + @Test + fun transitionStateLogging_dreaming_enterCommunalHub() = + testScope.runTest { + // Dreaming + keyguardRepository.setDreamingWithOverlay(true) + + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) + communalSceneInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START) + + // Finish transition to communal + transitionState.value = idle(CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + } + + @Test + fun transitionStateLogging_dreaming_cancelEnteringCommunalHub() = + testScope.runTest { + // Dreaming + keyguardRepository.setDreamingWithOverlay(true) + + // Transition state is default (non-communal) + val transitionState = + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Default)) + communalSceneInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify nothing is logged from the default state + verify(uiEventLogger, never()).log(any()) + + // Start transition to communal + transitionState.value = transition(to = CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START) + + // Cancel the transition + transitionState.value = idle(CommunalScenes.Default) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_dreaming_exitCommunalHub() = + testScope.runTest { + // Dreaming + keyguardRepository.setDreamingWithOverlay(true) + + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) + communalSceneInteractor.setTransitionState(transitionState) + runCurrent() + + // Verify SHOWN is logged when it's the default state + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + + // Start transition from communal + transitionState.value = transition(from = CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START) + + // Finish transition to communal + transitionState.value = idle(CommunalScenes.Default) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH) + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + + @Test + fun transitionStateLogging_dreaming_cancelExitingCommunalHub() = + testScope.runTest { + // Dreaming + keyguardRepository.setDreamingWithOverlay(true) + + // Transition state is communal + val transitionState = + MutableStateFlow<ObservableTransitionState>(idle(CommunalScenes.Communal)) + communalSceneInteractor.setTransitionState(transitionState) + runCurrent() + + // Clear the initial SHOWN event from the logger + clearInvocations(uiEventLogger) + + // Start transition from communal + transitionState.value = transition(from = CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START) + + // Cancel the transition + transitionState.value = idle(CommunalScenes.Communal) + runCurrent() + + // Verify UiEvent logged + verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL) + + // Verify neither SHOWN nor GONE is logged + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_SHOWN) + verify(uiEventLogger, never()).log(CommunalUiEvent.COMMUNAL_HUB_GONE) + } + private fun transition( from: SceneKey = CommunalScenes.Default, to: SceneKey = CommunalScenes.Default, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt index 537ca033f4b1..82918a569990 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt @@ -94,6 +94,30 @@ class CommunalMetricsLoggerTest : SysuiTestCase() { } @Test + fun logTapWidget_componentNotLoggable_doNotLog() { + underTest.logTapWidget( + componentName = "com.yellow.package/my_test_widget", + rank = 2, + ) + verify(statsLogProxy, never()) + .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt()) + } + + @Test + fun logTapWidget_componentLoggable_logRemoveEvent() { + underTest.logTapWidget( + componentName = "com.red.package/my_test_widget", + rank = 2, + ) + verify(statsLogProxy) + .writeCommunalHubWidgetEventReported( + SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP, + "com.red.package/my_test_widget", + 2, + ) + } + + @Test fun logWidgetsSnapshot_logOnlyLoggableComponents() { val statsEvents = mutableListOf<StatsEvent>() underTest.logWidgetsSnapshot( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index d862a21d8e99..86871edf98f5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal.view.viewmodel +import android.content.ComponentName import android.content.pm.UserInfo import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings @@ -41,6 +42,7 @@ import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.interactor.communalTutorialInteractor import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.log.CommunalMetricsLogger import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS @@ -107,6 +109,7 @@ import platform.test.runner.parameterized.Parameters @RunWith(ParameterizedAndroidJunit4::class) class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { @Mock private lateinit var mediaHost: MediaHost + @Mock private lateinit var metricsLogger: CommunalMetricsLogger private val kosmos = testKosmos() private val testScope = kosmos.testScope @@ -170,7 +173,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { kosmos.communalTutorialInteractor, kosmos.shadeInteractor, mediaHost, - logcatLogBuffer("CommunalViewModelTest") + logcatLogBuffer("CommunalViewModelTest"), + metricsLogger, ) } @@ -746,6 +750,12 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { verify(communalInteractor).setScrollPosition(eq(index), eq(offset)) } + @Test + fun onTapWidget_logEvent() { + underTest.onTapWidget(ComponentName("test_pkg", "test_cls"), priority = 10) + verify(metricsLogger).logTapWidget("test_pkg/test_cls", rank = 10) + } + private suspend fun setIsMainUser(isMainUser: Boolean) { val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO with(userRepository) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt new file mode 100644 index 000000000000..5b629b91eb45 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.widgets + +import android.content.ComponentName +import android.content.Intent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +@ExperimentalCoroutinesApi +@SmallTest +@RunWith(AndroidJUnit4::class) +class EditWidgetsActivityStarterTest : SysuiTestCase() { + private val activityStarter = mock<ActivityStarter>() + private val kosmos = testKosmos() + + private lateinit var component: ComponentName + private lateinit var underTest: EditWidgetsActivityStarter + + @Before + fun setUp() { + component = ComponentName(context, EditWidgetsActivity::class.java) + underTest = + EditWidgetsActivityStarterImpl( + context.applicationContext, + activityStarter, + ) + } + + @Test + fun activityLaunch_intentIsWellFormed() { + with(kosmos) { + testScope.runTest { + underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = true) + + val captor = argumentCaptor<Intent>() + verify(activityStarter) + .startActivityDismissingKeyguard(captor.capture(), eq(true), eq(true), any()) + assertThat(captor.lastValue.component).isEqualTo(component) + assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0) + assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK) + .isNotEqualTo(0) + assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY)) + .isEqualTo(TEST_PRESELECTED_KEY) + assertThat( + captor.lastValue.extras?.getBoolean( + EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START + ) + ) + .isEqualTo(true) + + underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = false) + + verify(activityStarter, times(2)) + .startActivityDismissingKeyguard(captor.capture(), eq(true), eq(true), any()) + assertThat(captor.lastValue.component).isEqualTo(component) + assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0) + assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK) + .isNotEqualTo(0) + assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY)) + .isEqualTo(TEST_PRESELECTED_KEY) + assertThat( + captor.lastValue.extras?.getBoolean( + EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START + ) + ) + .isEqualTo(false) + } + } + } + + companion object { + const val TEST_PRESELECTED_KEY = "test-key" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index 60c9bb03b68e..4c24ce2ad8c5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -54,6 +54,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable +import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutEngine @@ -660,6 +661,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { verify(mDreamOverlayCallback).onRedirectWake(true) client.onWakeRequested() verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), isNull()) + verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt index b885800ab964..783e3b5ba810 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -33,16 +33,21 @@ package com.android.systemui.keyguard.domain.interactor import android.os.PowerManager +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.FlagsParameterization import android.service.dream.dreamManager -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState -import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR +import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.CommunalTransitionKeys import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -51,6 +56,7 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockMode import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest @@ -69,15 +75,22 @@ import org.junit.runner.RunWith import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.reset import org.mockito.Mockito.spy +import org.mockito.kotlin.clearInvocations +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidJUnit4::class) -class FromDozingTransitionInteractorTest : SysuiTestCase() { +@RunWith(ParameterizedAndroidJunit4::class) +@EnableFlags(FLAG_COMMUNAL_HUB) +class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() { private val kosmos = testKosmos().apply { this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository()) + this.fakeCommunalSceneRepository = + spy(FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope)) } private val testScope = kosmos.testScope @@ -86,6 +99,18 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { private lateinit var powerInteractor: PowerInteractor private lateinit var transitionRepository: FakeKeyguardTransitionRepository + companion object { + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) + } + } + + init { + mSetFlagsRule.setFlagsParameterization(flags!!) + } + @Before fun setup() { powerInteractor = kosmos.powerInteractor @@ -108,7 +133,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToLockscreen_onWakeup() = testScope.runTest { powerInteractor.setAwakeForTest() @@ -123,7 +148,8 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR, Flags.FLAG_COMMUNAL_HUB) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) fun testTransitionToLockscreen_onPowerButtonPress_canDream_glanceableHubAvailable() = testScope.runTest { whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true) @@ -143,7 +169,25 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_COMMUNAL_SCENE_KTF_REFACTOR) + fun testTransitionToLockscreen_onPowerButtonPress_canDream_ktfRefactor() = + testScope.runTest { + whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true) + kosmos.setCommunalAvailable(true) + runCurrent() + clearInvocations(kosmos.fakeCommunalSceneRepository) + + powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON) + runCurrent() + + // If dreaming is possible and communal is available, then we should transition to + // GLANCEABLE_HUB when waking up due to power button press. + verify(kosmos.fakeCommunalSceneRepository) + .changeScene(CommunalScenes.Communal, CommunalTransitionKeys.Immediately) + } + + @Test + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToLockscreen_onPowerButtonPress_canNotDream_glanceableHubAvailable() = testScope.runTest { whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false) @@ -163,7 +207,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToLockscreen_onPowerButtonPress_canNDream_glanceableHubNotAvailable() = testScope.runTest { whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true) @@ -183,7 +227,8 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) + @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() = testScope.runTest { kosmos.fakeCommunalSceneRepository.setTransitionState( @@ -203,7 +248,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop() = testScope.runTest { kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) @@ -219,7 +264,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop_evenIfIdleOnCommunal() = testScope.runTest { kosmos.fakeCommunalSceneRepository.setTransitionState( @@ -240,7 +285,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) @Suppress("ktlint:standard:max-line-length") fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() = testScope.runTest { @@ -254,7 +299,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() = testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) @@ -268,7 +313,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() = testScope.runTest { powerInteractor.setAwakeForTest() @@ -306,7 +351,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) @Suppress("ktlint:standard:max-line-length") fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() = testScope.runTest { @@ -342,7 +387,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR) fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() = testScope.runTest { powerInteractor.setAwakeForTest() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt index 65aa825e487d..74243208cd98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt @@ -32,11 +32,13 @@ package com.android.systemui.keyguard.domain.interactor +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.shared.model.CommunalScenes @@ -49,13 +51,13 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.testKosmos -import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.reset import org.mockito.Mockito.spy @@ -108,6 +110,7 @@ class FromOccludedTransitionInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR) fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() = testScope.runTest { kosmos.fakeCommunalSceneRepository.setTransitionState( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 3fd1c20c0560..d9708a4f5506 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -362,6 +362,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test + @DisableSceneContainer fun dreamingLockscreenHostedToLockscreen() = testScope.runTest { // GIVEN a device dreaming with the lockscreen hosted dream and not dozing @@ -449,6 +450,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test + @DisableSceneContainer fun dreamingLockscreenHostedToDozing() = testScope.runTest { // GIVEN a device is dreaming with lockscreen hosted dream @@ -480,6 +482,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test + @DisableSceneContainer fun dreamingLockscreenHostedToOccluded() = testScope.runTest { // GIVEN device is dreaming with lockscreen hosted dream and not occluded @@ -977,6 +980,7 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest } @Test + @DisableSceneContainer fun alternateBouncerToGlanceableHub() = testScope.runTest { // GIVEN the device is idle on the glanceable hub diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 3db9ef1eca71..bca83f0f1ff7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -257,7 +257,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel { return LockscreenSceneViewModel( - applicationScope = testScope.backgroundScope, deviceEntryInteractor = kosmos.deviceEntryInteractor, communalInteractor = kosmos.communalInteractor, touchHandling = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 924962187ced..3ca802eb7091 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -93,7 +93,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { sceneContainerStartable.start() underTest = QuickSettingsSceneViewModel( - applicationScope = testScope.backgroundScope, brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel, shadeHeaderViewModel = kosmos.shadeHeaderViewModel, qsSceneAdapter = qsFlexiglassAdapter, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 39b366226987..228d61accce4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -143,7 +143,6 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { private val lockscreenSceneViewModel by lazy { LockscreenSceneViewModel( - applicationScope = testScope.backgroundScope, deviceEntryInteractor = deviceEntryInteractor, communalInteractor = communalInteractor, touchHandling = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt index 0ab6a8250dcf..a4992e2e61d4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModelTest.kt @@ -53,7 +53,6 @@ class GoneSceneViewModelTest : SysuiTestCase() { fun setUp() { underTest = GoneSceneViewModel( - applicationScope = testScope.backgroundScope, shadeInteractor = kosmos.shadeInteractor, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index da22c6d7419d..343b6bd3472c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -25,6 +25,7 @@ import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.systemui.SysuiTestCase +import com.android.systemui.activatable.activateIn import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository @@ -59,6 +60,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -78,6 +80,11 @@ class ShadeSceneViewModelTest : SysuiTestCase() { private val underTest: ShadeSceneViewModel by lazy { kosmos.shadeSceneViewModel } + @Before + fun setUp() { + underTest.activateIn(testScope) + } + @Test fun upTransitionSceneKey_deviceLocked_lockScreen() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index 23b28e37a4db..1df2553d0eb8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -27,6 +27,8 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes @@ -64,6 +66,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { private val placeholderViewModel by lazy { kosmos.notificationsPlaceholderViewModel } private val scrollViewModel by lazy { kosmos.notificationScrollViewModel } private val sceneInteractor by lazy { kosmos.sceneInteractor } + private val fakeKeyguardRepository by lazy { kosmos.fakeKeyguardRepository } private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource } @Test @@ -73,9 +76,11 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { val leftOffset = MutableStateFlow(0) val shape by collectLastValue(scrollViewModel.shadeScrimShape(radius, leftOffset)) + // When: receive scrim bounds placeholderViewModel.onScrimBoundsChanged( ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f) ) + // Then: shape is updated assertThat(shape) .isEqualTo( ShadeScrimShape( @@ -86,11 +91,13 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { ) ) + // When: receive new scrim bounds leftOffset.value = 200 radius.value = 24 placeholderViewModel.onScrimBoundsChanged( ShadeScrimBounds(left = 210f, top = 200f, right = 300f, bottom = 550f) ) + // Then: shape is updated assertThat(shape) .isEqualTo( ShadeScrimShape( @@ -100,6 +107,16 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { bottomRadius = 0 ) ) + + // When: QuickSettings shows up full screen + fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE) + val transitionState = + MutableStateFlow<ObservableTransitionState>( + ObservableTransitionState.Idle(Scenes.QuickSettings) + ) + sceneInteractor.setTransitionState(transitionState) + // Then: shape is null + assertThat(shape).isNull() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt index 8810ade1d851..7b87aeb60c13 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_LOW -import android.os.UserHandle import android.platform.test.annotations.EnableFlags import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -475,20 +474,6 @@ class LockScreenMinimalismCoordinatorTest : SysuiTestCase() { val collectionListener: NotifCollectionListener = argumentCaptor { verify(notifPipeline).addCollectionListener(capture()) }.lastValue - - var showOnlyUnseenNotifsOnKeyguardSetting: Boolean - get() = - fakeSettings.getIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - UserHandle.USER_CURRENT, - ) == 1 - set(value) { - fakeSettings.putIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - if (value) 1 else 2, - UserHandle.USER_CURRENT, - ) - } } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt index 6ddc07432a16..3fd9c2160ce2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt @@ -18,23 +18,26 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification -import android.os.UserHandle import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.dump.DumpManager +import com.android.systemui.dump.dumpManager import com.android.systemui.flags.andSceneContainer -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer -import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.setTransition +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -42,12 +45,15 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener -import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor +import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting +import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener +import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.testKosmos import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.settings.fakeSettings import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineScope @@ -72,13 +78,23 @@ import platform.test.runner.parameterized.Parameters @RunWith(ParameterizedAndroidJunit4::class) class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() { - private val kosmos = Kosmos() + private val kosmos = + testKosmos().apply { + testDispatcher = UnconfinedTestDispatcher() + statusBarStateController = mock() + fakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1) + } + + private val keyguardRepository + get() = kosmos.fakeKeyguardRepository + + private val keyguardTransitionRepository + get() = kosmos.fakeKeyguardTransitionRepository + + private val statusBarStateController + get() = kosmos.statusBarStateController - private val headsUpManager: HeadsUpManager = mock() - private val keyguardRepository = FakeKeyguardRepository() - private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository private val notifPipeline: NotifPipeline = mock() - private val statusBarStateController: StatusBarStateController = mock() init { mSetFlagsRule.setFlagsParameterization(flags) @@ -252,7 +268,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu collectionListener.onEntryAdded(fakeEntry) // GIVEN: The setting for filtering unseen notifications is disabled - showOnlyUnseenNotifsOnKeyguardSetting = false + kosmos.lockScreenShowOnlyUnseenNotificationsSetting = false // GIVEN: The pipeline has registered the unseen filter for invalidation val invalidationListener: Pluggable.PluggableListener<NotifFilter> = mock() @@ -266,7 +282,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse() // WHEN: The secure setting is changed - showOnlyUnseenNotifsOnKeyguardSetting = true + kosmos.lockScreenShowOnlyUnseenNotificationsSetting = true // THEN: The pipeline is invalidated verify(invalidationListener).onPluggableInvalidated(same(unseenFilter), any()) @@ -607,34 +623,25 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu private fun runKeyguardCoordinatorTest( testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit ) { - val testDispatcher = UnconfinedTestDispatcher() - val testScope = TestScope(testDispatcher) - val fakeSettings = - FakeSettings().apply { - putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1) - } - val seenNotificationsInteractor = - SeenNotificationsInteractor(ActiveNotificationListRepository()) val keyguardCoordinator = OriginalUnseenKeyguardCoordinator( - testDispatcher, - mock<DumpManager>(), - headsUpManager, - keyguardRepository, - kosmos.keyguardTransitionInteractor, - KeyguardCoordinatorLogger(logcatLogBuffer()), - testScope.backgroundScope, - fakeSettings, - seenNotificationsInteractor, - statusBarStateController, + dumpManager = kosmos.dumpManager, + headsUpManager = kosmos.headsUpManager, + keyguardRepository = kosmos.keyguardRepository, + keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, + logger = KeyguardCoordinatorLogger(logcatLogBuffer()), + scope = kosmos.testScope.backgroundScope, + seenNotificationsInteractor = kosmos.seenNotificationsInteractor, + statusBarStateController = kosmos.statusBarStateController, + sceneInteractor = kosmos.sceneInteractor, ) keyguardCoordinator.attach(notifPipeline) - testScope.runTest { + kosmos.testScope.runTest { KeyguardCoordinatorTestScope( keyguardCoordinator, - testScope, - seenNotificationsInteractor, - fakeSettings, + kosmos.testScope, + kosmos.seenNotificationsInteractor, + kosmos.fakeSettings, ) .testBlock() } @@ -656,21 +663,8 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu argumentCaptor { verify(notifPipeline).addCollectionListener(capture()) }.lastValue val onHeadsUpChangedListener: OnHeadsUpChangedListener - get() = argumentCaptor { verify(headsUpManager).addListener(capture()) }.lastValue - - var showOnlyUnseenNotifsOnKeyguardSetting: Boolean get() = - fakeSettings.getIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - UserHandle.USER_CURRENT, - ) == 1 - set(value) { - fakeSettings.putIntForUser( - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - if (value) 1 else 2, - UserHandle.USER_CURRENT, - ) - } + argumentCaptor { verify(kosmos.headsUpManager).addListener(capture()) }.lastValue } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt new file mode 100644 index 000000000000..2159b864d2a2 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import android.platform.test.annotations.EnableFlags +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class SeenNotificationsInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val underTest + get() = kosmos.seenNotificationsInteractor + + @Test + fun testNoFilteredOutSeenNotifications() = runTest { + val hasFilteredOutSeenNotifications by + collectLastValue(underTest.hasFilteredOutSeenNotifications) + + underTest.setHasFilteredOutSeenNotifications(false) + + assertThat(hasFilteredOutSeenNotifications).isFalse() + } + + @Test + fun testHasFilteredOutSeenNotifications() = runTest { + val hasFilteredOutSeenNotifications by + collectLastValue(underTest.hasFilteredOutSeenNotifications) + + underTest.setHasFilteredOutSeenNotifications(true) + + assertThat(hasFilteredOutSeenNotifications).isTrue() + } + + @Test + @EnableFlags(NotificationMinimalismPrototype.FLAG_NAME) + fun topOngoingAndUnseenNotification() = runTest { + val entry1 = NotificationEntryBuilder().setTag("entry1").build() + val entry2 = NotificationEntryBuilder().setTag("entry2").build() + + underTest.setTopOngoingNotification(null) + underTest.setTopUnseenNotification(null) + + assertThat(underTest.isTopOngoingNotification(entry1)).isFalse() + assertThat(underTest.isTopOngoingNotification(entry2)).isFalse() + assertThat(underTest.isTopUnseenNotification(entry1)).isFalse() + assertThat(underTest.isTopUnseenNotification(entry2)).isFalse() + + underTest.setTopOngoingNotification(entry1) + underTest.setTopUnseenNotification(entry2) + + assertThat(underTest.isTopOngoingNotification(entry1)).isTrue() + assertThat(underTest.isTopOngoingNotification(entry2)).isFalse() + assertThat(underTest.isTopUnseenNotification(entry1)).isFalse() + assertThat(underTest.isTopUnseenNotification(entry2)).isTrue() + } + + fun testShowOnlyUnseenNotifsOnKeyguardSetting() = runTest { + val settingEnabled by + collectLastValue(underTest.isLockScreenShowOnlyUnseenNotificationsEnabled()) + + kosmos.lockScreenShowOnlyUnseenNotificationsSetting = false + testScheduler.runCurrent() + assertThat(settingEnabled).isFalse() + + kosmos.lockScreenShowOnlyUnseenNotificationsSetting = true + testScheduler.runCurrent() + assertThat(settingEnabled).isTrue() + } +} diff --git a/packages/SystemUI/res/color/disconnected_network_primary_color.xml b/packages/SystemUI/res/color/disconnected_network_primary_color.xml new file mode 100644 index 000000000000..536bf78b7b60 --- /dev/null +++ b/packages/SystemUI/res/color/disconnected_network_primary_color.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2024 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> + <item android:color="?androidprv:attr/materialColorPrimaryContainer" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index 22d156da7580..0029180932ee 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -170,8 +170,7 @@ android:layout_height="28dp" android:layout_marginStart="7dp" android:layout_marginEnd="16dp" - android:layout_gravity="center_vertical" - android:background="?android:attr/textColorSecondary"/> + android:layout_gravity="center_vertical"/> <FrameLayout android:layout_width="@dimen/settingslib_switch_track_width" diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index 154397d2b4a1..dc56097eaf3d 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -23,8 +23,7 @@ android:layout_gravity="center_vertical|start" android:layout_marginStart="5dp" > - <!-- TODO(b/332662551): Update this content description when this supports more than just - phone calls. --> + <!-- TODO(b/354930838): Fix these paddings for the new screen chips. --> <com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer android:id="@+id/ongoing_activity_chip_background" android:layout_width="wrap_content" @@ -32,18 +31,18 @@ android:layout_gravity="center_vertical" android:gravity="center" android:background="@drawable/ongoing_activity_chip_bg" - android:paddingStart="@dimen/ongoing_activity_chip_side_padding" - android:paddingEnd="@dimen/ongoing_activity_chip_side_padding" + android:paddingStart="@dimen/ongoing_activity_chip_side_padding_with_notif_icon" + android:paddingEnd="@dimen/ongoing_activity_chip_side_padding_with_notif_icon" android:minWidth="@dimen/min_clickable_item_size" > <ImageView android:src="@*android:drawable/ic_phone" android:id="@+id/ongoing_activity_chip_icon" - android:contentDescription="@string/ongoing_phone_call_content_description" android:layout_width="@dimen/ongoing_activity_chip_icon_size" android:layout_height="@dimen/ongoing_activity_chip_icon_size" android:tint="?android:attr/colorPrimary" + android:visibility="gone" /> <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever @@ -54,7 +53,7 @@ android:layout_height="wrap_content" android:singleLine="true" android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" + android:paddingEnd="@dimen/ongoing_activity_chip_text_end_padding_with_notif_icon" android:textAppearance="@android:style/TextAppearance.Material.Small" android:fontFamily="@*android:string/config_headlineFontFamily" android:textColor="?android:attr/colorPrimary" diff --git a/packages/SystemUI/res/raw/trackpad_back_edu.json b/packages/SystemUI/res/raw/trackpad_back_edu.json index 793833d79cd2..908d26ff40cd 100644 --- a/packages/SystemUI/res/raw/trackpad_back_edu.json +++ b/packages/SystemUI/res/raw/trackpad_back_edu.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":900,"w":554,"h":564,"nm":"Trackpad-JSON_BackGesture","ddd":0,"assets":[{"id":"comp_0","nm":"Back_LeftDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":79},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiary","cl":"onTertiary","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.308,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.009,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.291,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.138,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.452,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[16.757,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.542,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.002,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.238,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.308,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.331,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.006,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.308,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.382,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.657,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.165,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.794,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.403,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.942,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.411,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.822,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.186,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.511,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.803,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.069,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.311,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.534,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.739,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.928,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.103,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.267,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.419,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.56,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.693,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.816,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.932,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.041,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.142,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.238,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.327,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.411,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.489,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.563,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.632,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.696,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.756,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.812,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.864,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.913,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.958,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.039,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.074,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.107,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.137,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.164,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.188,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.21,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.23,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.247,0,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.274,0,0],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.305,0,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[0.15]},"t":160,"s":[-14]},{"t":189,"s":[0]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[32,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":8}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[0,-20],[4,-24],[4,-24],[8,-20],[8,20],[4,24],[4,24],[0,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[0,-19.3],[4.7,-24],[4.7,-24],[9.401,-19.3],[9.401,19.3],[4.7,24],[4.7,24],[0,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[0,-15.017],[8.983,-24],[8.983,-24],[17.967,-15.017],[17.967,15.017],[8.983,24],[8.983,24],[0,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[0,-10.171],[13.829,-24],[13.829,-24],[27.659,-10.171],[27.659,10.171],[13.829,24],[13.829,24],[0,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[0,-7.856],[16.144,-24],[16.144,-24],[32.287,-7.856],[32.287,7.856],[16.144,24],[16.144,24],[0,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[0,-6.551],[17.449,-24],[17.449,-24],[34.898,-6.551],[34.898,6.551],[17.449,24],[17.449,24],[0,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[0,-5.766],[18.234,-24],[18.234,-24],[36.467,-5.766],[36.467,5.766],[18.234,24],[18.234,24],[0,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[0,-5.306],[18.694,-24],[18.694,-24],[37.388,-5.306],[37.388,5.306],[18.694,24],[18.694,24],[0,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[0,-5.07],[18.93,-24],[18.93,-24],[37.861,-5.07],[37.861,5.07],[18.93,24],[18.93,24],[0,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[0,-5],[19,-24],[19,-24],[38,-5],[38,5],[19,24],[19,24],[0,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[0,-1.977],[22.023,-24],[22.023,-24],[44.045,-1.977],[44.045,1.977],[22.023,24],[22.023,24],[0,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[0,-0.302],[23.698,-24],[23.698,-24],[47.396,-0.302],[47.396,0.302],[23.698,24],[23.698,24],[0,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24,-24],[48,0],[48,0],[24,24],[24,24],[0,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.149,-24],[48.149,0],[48.149,0],[24.149,24],[24,24],[0,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.698,-24],[48.698,0],[48.698,0],[24.698,24],[24,24],[0,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[25.714,-24],[49.714,0],[49.714,0],[25.714,24],[24,24],[0,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[26.973,-24],[50.973,0],[50.973,0],[26.973,24],[24,24],[0,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[28.19,-24],[52.19,0],[52.19,0],[28.19,24],[24,24],[0,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[29.268,-24],[53.268,0],[53.268,0],[29.268,24],[24,24],[0,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[30.206,-24],[54.206,0],[54.206,0],[30.206,24],[24,24],[0,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.028,-24],[55.028,0],[55.028,0],[31.028,24],[24,24],[0,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.755,-24],[55.755,0],[55.755,0],[31.755,24],[24,24],[0,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.405,-24],[56.405,0],[56.405,0],[32.405,24],[24,24],[0,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.99,-24],[56.99,0],[56.99,0],[32.99,24],[24,24],[0,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[33.522,-24],[57.522,0],[57.522,0],[33.522,24],[24,24],[0,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.006,-24],[58.006,0],[58.006,0],[34.006,24],[24,24],[0,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.451,-24],[58.451,0],[58.451,0],[34.451,24],[24,24],[0,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.861,-24],[58.861,0],[58.861,0],[34.861,24],[24,24],[0,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.24,-24],[59.24,0],[59.24,0],[35.24,24],[24,24],[0,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.591,-24],[59.591,0],[59.591,0],[35.591,24],[24,24],[0,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.917,-24],[59.917,0],[59.917,0],[35.917,24],[24,24],[0,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.221,-24],[60.221,0],[60.221,0],[36.221,24],[24,24],[0,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.504,-24],[60.504,0],[60.504,0],[36.504,24],[24,24],[0,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.769,-24],[60.769,0],[60.769,0],[36.769,24],[24,24],[0,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.017,-24],[61.017,0],[61.017,0],[37.017,24],[24,24],[0,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.248,-24],[61.248,0],[61.248,0],[37.248,24],[24,24],[0,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.465,-24],[61.465,0],[61.465,0],[37.465,24],[24,24],[0,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.669,-24],[61.669,0],[61.669,0],[37.669,24],[24,24],[0,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.859,-24],[61.859,0],[61.859,0],[37.859,24],[24,24],[0,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.038,-24],[62.038,0],[62.038,0],[38.038,24],[24,24],[0,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.205,-24],[62.205,0],[62.205,0],[38.205,24],[24,24],[0,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.362,-24],[62.362,0],[62.362,0],[38.362,24],[24,24],[0,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.509,-24],[62.509,0],[62.509,0],[38.509,24],[24,24],[0,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.647,-24],[62.647,0],[62.647,0],[38.647,24],[24,24],[0,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.776,-24],[62.776,0],[62.776,0],[38.776,24],[24,24],[0,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.896,-24],[62.896,0],[62.896,0],[38.896,24],[24,24],[0,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.008,-24],[63.008,0],[63.008,0],[39.008,24],[24,24],[0,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.113,-24],[63.113,0],[63.113,0],[39.113,24],[24,24],[0,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.21,-24],[63.21,0],[63.21,0],[39.21,24],[24,24],[0,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.3,-24],[63.3,0],[63.3,0],[39.3,24],[24,24],[0,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.384,-24],[63.384,0],[63.384,0],[39.384,24],[24,24],[0,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.461,-24],[63.461,0],[63.461,0],[39.461,24],[24,24],[0,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.532,-24],[63.532,0],[63.532,0],[39.532,24],[24,24],[0,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.597,-24],[63.597,0],[63.597,0],[39.597,24],[24,24],[0,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.657,-24],[63.657,0],[63.657,0],[39.657,24],[24,24],[0,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.711,-24],[63.711,0],[63.711,0],[39.711,24],[24,24],[0,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.76,-24],[63.76,0],[63.76,0],[39.76,24],[24,24],[0,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.804,-24],[63.804,0],[63.804,0],[39.804,24],[24,24],[0,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.843,-24],[63.843,0],[63.843,0],[39.843,24],[24,24],[0,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.877,-24],[63.877,0],[63.877,0],[39.877,24],[24,24],[0,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.907,-24],[63.907,0],[63.907,0],[39.907,24],[24,24],[0,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.932,-24],[63.932,0],[63.932,0],[39.932,24],[24,24],[0,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.971,-24],[63.971,0],[63.971,0],[39.971,24],[24,24],[0,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":450,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[277.263,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.191,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.194,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.275,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.841,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[297.7,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.568,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.993,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.914,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.504,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.464,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.661,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.021,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.499,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.068,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.705,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.401,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.143,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.925,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.741,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.585,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.345,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.074,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277.263,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.936,197.788],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.7,199.014],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.071,202.033],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.438,206.77],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.978,211.581],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[290.807,215.785],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.487,219.444],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.718,222.659],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.317,225.519],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[284.171,228.085],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.211,230.396],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.392,232.474],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.682,234.334],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.059,235.992],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.506,237.461],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.012,238.754],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.568,239.881],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.169,240.855],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.809,241.684],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.487,242.379],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.199,242.951],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.943,243.409],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.72,243.76],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.528,243.874],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.369,243.701],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.24,243.336],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.142,242.847],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.073,242.284],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.033,241.684],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.85,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.285,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.162,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.05,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.986,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.592,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.166,217.207],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.184,212.309],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.238,209.328],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.904,207.237],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.379,205.654],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.741,204.399],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.029,203.375],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.267,202.521],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.466,201.799],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.638,201.182],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.788,200.65],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.921,200.189],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.041,199.789],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.149,199.439],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.246,199.134],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.337,198.867],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.419,198.634],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.495,198.431],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.566,198.255],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.632,198.103],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.692,197.973],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.748,197.862],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.86,197.692],"t":410,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[-28.906,14.531,0],"ti":[7.183,-8.833,0]},{"t":280,"s":[-43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[-43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[-25.86,31.8,0],"to":[7.183,-8.833,0],"ti":[-7.167,9.833,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":450,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[554,564]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"container for media","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[339.937,151.75,0]},"a":{"a":0,"k":[339.937,151.75,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[334,279]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[334,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":16},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,171.125,0]},"a":{"a":0,"k":[82,171.125,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 2","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,140,0]},"a":{"a":0,"k":[82,140.938,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Search","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"header","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,171]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"block","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 13","np":8,"mn":"ADBE Drop Shadow","ix":13,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 14","np":8,"mn":"ADBE Drop Shadow","ix":14,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"Scale Up","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":250,"s":[85,85,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":256,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":286,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[96,96,100]},{"t":416,"s":[90,90,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Back_RightDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":476},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiary","cl":"onTertiary","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.692,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.392,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.675,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.521,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-16.835,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.141,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.925,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.386,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.622,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.692,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-22.714,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.39,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.692,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.766,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.041,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.549,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.178,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.787,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.326,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.795,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.206,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.57,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.894,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.187,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.453,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.695,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.917,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.122,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.312,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.487,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.65,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.802,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.944,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.076,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.2,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.316,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.424,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.526,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.621,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.711,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.795,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.873,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.947,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.015,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.08,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.14,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.196,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.248,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.297,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.342,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.384,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.422,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.458,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.49,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.52,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.547,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.572,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.594,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.613,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.645,0,0],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.677,0,0],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[-0.15]},"t":160,"s":[13.981]},{"t":189,"s":[-0.019]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[-31.019,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":4}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[-8,-20],[-4,-24],[-4,-24],[0,-20],[0,20],[-4,24],[-4,24],[-8,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[-9.401,-19.3],[-4.7,-24],[-4.7,-24],[0,-19.3],[0,19.3],[-4.7,24],[-4.7,24],[-9.401,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[-17.967,-15.017],[-8.983,-24],[-8.983,-24],[0,-15.017],[0,15.017],[-8.983,24],[-8.983,24],[-17.967,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[-27.659,-10.171],[-13.829,-24],[-13.829,-24],[0,-10.171],[0,10.171],[-13.829,24],[-13.829,24],[-27.659,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[-32.287,-7.856],[-16.144,-24],[-16.144,-24],[0,-7.856],[0,7.856],[-16.144,24],[-16.144,24],[-32.287,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[-34.898,-6.551],[-17.449,-24],[-17.449,-24],[0,-6.551],[0,6.551],[-17.449,24],[-17.449,24],[-34.898,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[-36.467,-5.766],[-18.234,-24],[-18.234,-24],[0,-5.766],[0,5.766],[-18.234,24],[-18.234,24],[-36.467,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[-37.388,-5.306],[-18.694,-24],[-18.694,-24],[0,-5.306],[0,5.306],[-18.694,24],[-18.694,24],[-37.388,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[-37.861,-5.07],[-18.93,-24],[-18.93,-24],[0,-5.07],[0,5.07],[-18.93,24],[-18.93,24],[-37.861,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[-38,-5],[-19,-24],[-19,-24],[0,-5],[0,5],[-19,24],[-19,24],[-38,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[-44.045,-1.977],[-22.023,-24],[-22.023,-24],[0,-1.977],[0,1.977],[-22.023,24],[-22.023,24],[-44.045,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[-47.396,-0.302],[-23.698,-24],[-23.698,-24],[0,-0.302],[0,0.302],[-23.698,24],[-23.698,24],[-47.396,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48,0],[-24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24,24],[-48,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.149,0],[-24.149,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.149,24],[-48.149,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.698,0],[-24.698,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.698,24],[-48.698,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-49.714,0],[-25.714,-24],[-24,-24],[0,0],[0,0],[-24,24],[-25.714,24],[-49.714,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-50.973,0],[-26.973,-24],[-24,-24],[0,0],[0,0],[-24,24],[-26.973,24],[-50.973,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-52.19,0],[-28.19,-24],[-24,-24],[0,0],[0,0],[-24,24],[-28.19,24],[-52.19,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-53.268,0],[-29.268,-24],[-24,-24],[0,0],[0,0],[-24,24],[-29.268,24],[-53.268,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-54.206,0],[-30.206,-24],[-24,-24],[0,0],[0,0],[-24,24],[-30.206,24],[-54.206,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.028,0],[-31.028,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.028,24],[-55.028,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.755,0],[-31.755,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.755,24],[-55.755,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.405,0],[-32.405,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.405,24],[-56.405,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.99,0],[-32.99,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.99,24],[-56.99,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-57.522,0],[-33.522,-24],[-24,-24],[0,0],[0,0],[-24,24],[-33.522,24],[-57.522,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.006,0],[-34.006,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.006,24],[-58.006,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.451,0],[-34.451,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.451,24],[-58.451,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.861,0],[-34.861,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.861,24],[-58.861,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.24,0],[-35.24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.24,24],[-59.24,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.591,0],[-35.591,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.591,24],[-59.591,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.917,0],[-35.917,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.917,24],[-59.917,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.221,0],[-36.221,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.221,24],[-60.221,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.504,0],[-36.504,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.504,24],[-60.504,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.769,0],[-36.769,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.769,24],[-60.769,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.017,0],[-37.017,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.017,24],[-61.017,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.248,0],[-37.248,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.248,24],[-61.248,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.465,0],[-37.465,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.465,24],[-61.465,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.669,0],[-37.669,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.669,24],[-61.669,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.859,0],[-37.859,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.859,24],[-61.859,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.038,0],[-38.038,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.038,24],[-62.038,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.205,0],[-38.205,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.205,24],[-62.205,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.362,0],[-38.362,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.362,24],[-62.362,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.509,0],[-38.509,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.509,24],[-62.509,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.647,0],[-38.647,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.647,24],[-62.647,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.776,0],[-38.776,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.776,24],[-62.776,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.896,0],[-38.896,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.896,24],[-62.896,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.008,0],[-39.008,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.008,24],[-63.008,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.113,0],[-39.113,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.113,24],[-63.113,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.21,0],[-39.21,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.21,24],[-63.21,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.3,0],[-39.3,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.3,24],[-63.3,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.384,0],[-39.384,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.384,24],[-63.384,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.461,0],[-39.461,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.461,24],[-63.461,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.532,0],[-39.532,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.532,24],[-63.532,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.597,0],[-39.597,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.597,24],[-63.597,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.657,0],[-39.657,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.657,24],[-63.657,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.711,0],[-39.711,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.711,24],[-63.711,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.76,0],[-39.76,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.76,24],[-63.76,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.804,0],[-39.804,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.804,24],[-63.804,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.843,0],[-39.843,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.843,24],[-63.843,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.877,0],[-39.877,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.877,24],[-63.877,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.907,0],[-39.907,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.907,24],[-63.907,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.932,0],[-39.932,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.932,24],[-63.932,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.971,0],[-39.971,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.971,24],[-63.971,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":498,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[276.737,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.809,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.805,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.726,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.159,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[256.3,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.431,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.009,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.087,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.496,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.536,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.339,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.98,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.502,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.933,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.295,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.599,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.857,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.075,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.259,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.415,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.655,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.926,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[276.737,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.159,197.525],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.792,197.842],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.233,199.672],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.542,204.005],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.216,208.989],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.399,213.457],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.667,217.355],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[269.364,220.773],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.685,223.812],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.743,226.538],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.606,228.991],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.321,231.197],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.92,233.179],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.427,234.953],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.858,236.532],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.225,237.926],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.539,239.152],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.808,240.219],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.038,241.138],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.233,241.918],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.398,242.568],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.537,243.099],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.653,243.517],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.824,243.574],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.884,243.254],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.928,242.802],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.959,242.269],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.976,241.685],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.15,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.715,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.839,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.95,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.015,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.407,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.954,217.108],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.005,212.156],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.975,209.156],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.314,207.064],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.834,205.487],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.461,204.24],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.159,203.226],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.905,202.385],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.691,201.676],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.503,201.072],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.339,200.553],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.193,200.105],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.061,199.716],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.941,199.376],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.831,199.079],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.729,198.82],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.636,198.594],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.551,198.398],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.472,198.228],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.399,198.082],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.333,197.956],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.209,197.759],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.063,197.577],"t":412,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[29.688,0.625,0],"ti":[0,0,0]},{"t":280,"s":[43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[25.86,31.8,0],"to":[0,0,0],"ti":[0,0,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":498,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[554,564]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"container for media","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Back_LeftDismiss","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":426,"st":-25,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Back_RightDismiss","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":426,"op":902,"st":401,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":900,"w":554,"h":564,"nm":"Trackpad-JSON_BackGesture-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Back_LeftDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":79},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.308,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.009,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.291,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.138,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.452,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[16.757,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.542,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.002,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.238,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[18.308,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.331,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.006,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.308,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.382,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.657,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.165,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[24.794,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.403,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.942,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.411,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.822,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.186,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.511,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[27.803,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.069,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.311,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.534,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.739,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.928,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.103,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.267,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.419,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.56,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.693,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.816,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.932,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.041,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.142,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.238,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.327,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.411,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.489,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.563,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.632,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.696,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.756,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.812,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.864,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.913,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[30.958,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.039,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.074,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.107,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.137,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.164,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.188,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.21,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.23,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.247,0,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.274,0,0],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.305,0,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[0.15]},"t":160,"s":[-14]},{"t":189,"s":[0]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[32,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":8}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[0,-20],[4,-24],[4,-24],[8,-20],[8,20],[4,24],[4,24],[0,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[0,-19.3],[4.7,-24],[4.7,-24],[9.401,-19.3],[9.401,19.3],[4.7,24],[4.7,24],[0,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[0,-15.017],[8.983,-24],[8.983,-24],[17.967,-15.017],[17.967,15.017],[8.983,24],[8.983,24],[0,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[0,-10.171],[13.829,-24],[13.829,-24],[27.659,-10.171],[27.659,10.171],[13.829,24],[13.829,24],[0,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[0,-7.856],[16.144,-24],[16.144,-24],[32.287,-7.856],[32.287,7.856],[16.144,24],[16.144,24],[0,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[0,-6.551],[17.449,-24],[17.449,-24],[34.898,-6.551],[34.898,6.551],[17.449,24],[17.449,24],[0,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[0,-5.766],[18.234,-24],[18.234,-24],[36.467,-5.766],[36.467,5.766],[18.234,24],[18.234,24],[0,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[0,-5.306],[18.694,-24],[18.694,-24],[37.388,-5.306],[37.388,5.306],[18.694,24],[18.694,24],[0,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[0,-5.07],[18.93,-24],[18.93,-24],[37.861,-5.07],[37.861,5.07],[18.93,24],[18.93,24],[0,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[0,-5],[19,-24],[19,-24],[38,-5],[38,5],[19,24],[19,24],[0,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[0,-1.977],[22.023,-24],[22.023,-24],[44.045,-1.977],[44.045,1.977],[22.023,24],[22.023,24],[0,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[0,-0.302],[23.698,-24],[23.698,-24],[47.396,-0.302],[47.396,0.302],[23.698,24],[23.698,24],[0,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24,-24],[48,0],[48,0],[24,24],[24,24],[0,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.149,-24],[48.149,0],[48.149,0],[24.149,24],[24,24],[0,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[24.698,-24],[48.698,0],[48.698,0],[24.698,24],[24,24],[0,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[25.714,-24],[49.714,0],[49.714,0],[25.714,24],[24,24],[0,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[26.973,-24],[50.973,0],[50.973,0],[26.973,24],[24,24],[0,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[28.19,-24],[52.19,0],[52.19,0],[28.19,24],[24,24],[0,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[29.268,-24],[53.268,0],[53.268,0],[29.268,24],[24,24],[0,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[30.206,-24],[54.206,0],[54.206,0],[30.206,24],[24,24],[0,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.028,-24],[55.028,0],[55.028,0],[31.028,24],[24,24],[0,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[31.755,-24],[55.755,0],[55.755,0],[31.755,24],[24,24],[0,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.405,-24],[56.405,0],[56.405,0],[32.405,24],[24,24],[0,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[32.99,-24],[56.99,0],[56.99,0],[32.99,24],[24,24],[0,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[33.522,-24],[57.522,0],[57.522,0],[33.522,24],[24,24],[0,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.006,-24],[58.006,0],[58.006,0],[34.006,24],[24,24],[0,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.451,-24],[58.451,0],[58.451,0],[34.451,24],[24,24],[0,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[34.861,-24],[58.861,0],[58.861,0],[34.861,24],[24,24],[0,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.24,-24],[59.24,0],[59.24,0],[35.24,24],[24,24],[0,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.591,-24],[59.591,0],[59.591,0],[35.591,24],[24,24],[0,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[35.917,-24],[59.917,0],[59.917,0],[35.917,24],[24,24],[0,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.221,-24],[60.221,0],[60.221,0],[36.221,24],[24,24],[0,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.504,-24],[60.504,0],[60.504,0],[36.504,24],[24,24],[0,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[36.769,-24],[60.769,0],[60.769,0],[36.769,24],[24,24],[0,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.017,-24],[61.017,0],[61.017,0],[37.017,24],[24,24],[0,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.248,-24],[61.248,0],[61.248,0],[37.248,24],[24,24],[0,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.465,-24],[61.465,0],[61.465,0],[37.465,24],[24,24],[0,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.669,-24],[61.669,0],[61.669,0],[37.669,24],[24,24],[0,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[37.859,-24],[61.859,0],[61.859,0],[37.859,24],[24,24],[0,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.038,-24],[62.038,0],[62.038,0],[38.038,24],[24,24],[0,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.205,-24],[62.205,0],[62.205,0],[38.205,24],[24,24],[0,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.362,-24],[62.362,0],[62.362,0],[38.362,24],[24,24],[0,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.509,-24],[62.509,0],[62.509,0],[38.509,24],[24,24],[0,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.647,-24],[62.647,0],[62.647,0],[38.647,24],[24,24],[0,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.776,-24],[62.776,0],[62.776,0],[38.776,24],[24,24],[0,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[38.896,-24],[62.896,0],[62.896,0],[38.896,24],[24,24],[0,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.008,-24],[63.008,0],[63.008,0],[39.008,24],[24,24],[0,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.113,-24],[63.113,0],[63.113,0],[39.113,24],[24,24],[0,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.21,-24],[63.21,0],[63.21,0],[39.21,24],[24,24],[0,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.3,-24],[63.3,0],[63.3,0],[39.3,24],[24,24],[0,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.384,-24],[63.384,0],[63.384,0],[39.384,24],[24,24],[0,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.461,-24],[63.461,0],[63.461,0],[39.461,24],[24,24],[0,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.532,-24],[63.532,0],[63.532,0],[39.532,24],[24,24],[0,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.597,-24],[63.597,0],[63.597,0],[39.597,24],[24,24],[0,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.657,-24],[63.657,0],[63.657,0],[39.657,24],[24,24],[0,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.711,-24],[63.711,0],[63.711,0],[39.711,24],[24,24],[0,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.76,-24],[63.76,0],[63.76,0],[39.76,24],[24,24],[0,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.804,-24],[63.804,0],[63.804,0],[39.804,24],[24,24],[0,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.843,-24],[63.843,0],[63.843,0],[39.843,24],[24,24],[0,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.877,-24],[63.877,0],[63.877,0],[39.877,24],[24,24],[0,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.907,-24],[63.907,0],[63.907,0],[39.907,24],[24,24],[0,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.932,-24],[63.932,0],[63.932,0],[39.932,24],[24,24],[0,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[0,0],[24,-24],[39.971,-24],[63.971,0],[63.971,0],[39.971,24],[24,24],[0,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":450,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[277.263,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.191,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.194,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.275,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.841,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[297.7,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.568,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.993,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.914,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.504,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.464,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.661,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.021,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.499,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.068,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.705,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.401,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.143,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.925,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.741,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.585,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.345,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.074,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277.263,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.43,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.681,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.85,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.058,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.313,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.63,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.023,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.517,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.151,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.992,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.175,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.778,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.586,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[287.564,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[289.63,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[291.671,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.578,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[295.298,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[296.823,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.167,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[299.353,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[300.405,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[301.343,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.187,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[302.949,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[303.641,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.271,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.846,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.373,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[305.858,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.306,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[306.72,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.103,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.459,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[307.789,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.096,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.382,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.648,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.895,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.126,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.34,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.54,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.726,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[309.901,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.063,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.352,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.599,197.5],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.903,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.196,197.5],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[310.936,197.788],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[308.7,199.014],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[304.071,202.033],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[298.438,206.77],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[293.978,211.581],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[290.807,215.785],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.487,219.444],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.718,222.659],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[285.317,225.519],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[284.171,228.085],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.211,230.396],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.392,232.474],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.682,234.334],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[281.059,235.992],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.506,237.461],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.012,238.754],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.568,239.881],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.169,240.855],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.809,241.684],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.487,242.379],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.199,242.951],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.943,243.409],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.72,243.76],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.528,243.874],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.369,243.701],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.24,243.336],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.142,242.847],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.073,242.284],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.033,241.684],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.02,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.85,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.285,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.162,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.05,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.986,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.592,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.166,217.207],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.184,212.309],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.238,209.328],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.904,207.237],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.379,205.654],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.741,204.399],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.029,203.375],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.267,202.521],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.466,201.799],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.638,201.182],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.788,200.65],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.921,200.189],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.041,199.789],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.149,199.439],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.246,199.134],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.337,198.867],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.419,198.634],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.495,198.431],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.566,198.255],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.632,198.103],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.692,197.973],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.748,197.862],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.86,197.692],"t":410,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[-28.906,14.531,0],"ti":[7.183,-8.833,0]},{"t":280,"s":[-43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[-43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[-25.86,31.8,0],"to":[7.183,-8.833,0],"ti":[-7.167,9.833,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":280,"s":[30,30]},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":280,"s":[30]},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":450,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[339.937,151.75,0]},"a":{"a":0,"k":[339.937,151.75,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[334,279]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[334,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":16},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,171.125,0]},"a":{"a":0,"k":[82,171.125,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 2","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,140,0]},"a":{"a":0,"k":[82,140.938,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Search","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"header","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,171]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"block","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"Scale Up","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":250,"s":[85,85,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":256,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":286,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[96,96,100]},{"t":416,"s":[90,90,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Back_RightDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":476},"y":{"a":0,"k":197}},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.08,0.08,0.08],"y":[0.47,0.47,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.999,0.999,0.999],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":254,"s":[105,105,100]},{"t":266,"s":[50,50,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":151,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":154,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":255,"s":[100]},{"t":258,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-0.692,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.692,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.392,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.675,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.521,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-16.835,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.141,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-18.925,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.386,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.622,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.692,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-22.714,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.39,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.692,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-24.766,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.041,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.549,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.178,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.787,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.326,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-27.795,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.206,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.57,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.894,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.187,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.453,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.695,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.917,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.122,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.312,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.487,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.65,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.802,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-30.944,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.076,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.2,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.316,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.424,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.526,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.621,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.711,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.795,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.873,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.947,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.015,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.08,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.14,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.196,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.248,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.297,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.342,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.384,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.422,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.458,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.49,0,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.52,0,0],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.547,0,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.572,0,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.594,0,0],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.613,0,0],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.645,0,0],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.677,0,0],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.36,"y":0},"t":150,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[5.459,5.2],[-3.459,0],[5.459,-5.2]],"c":false}]},{"i":{"x":0.02,"y":1},"o":{"x":0.167,"y":0.167},"t":152,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[4.779,4.88],[-3.459,0],[4.779,-4.88]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":159,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.12,"y":0},"t":162,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,7.2],[-3.459,0],[3.459,-7.2]],"c":false}]},{"t":217,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[3.459,9.2],[-3.459,0],[3.459,-9.2]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":4},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Vector 1","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":257,"s":[100]},{"t":260,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.06],"y":[-0.15]},"t":160,"s":[13.981]},{"t":189,"s":[-0.019]}]},"y":{"a":0,"k":0}},"a":{"a":0,"k":[-31.019,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"IndieCorners","np":21,"mn":"Pseudo/0.20784385308943532","ix":1,"en":1,"ef":[{"ty":7,"nm":"Align","mn":"Pseudo/0.20784385308943532-0001","ix":1,"v":{"a":0,"k":4}},{"ty":6,"nm":"Size","mn":"Pseudo/0.20784385308943532-0002","ix":2,"v":0},{"ty":0,"nm":"w","mn":"Pseudo/0.20784385308943532-0003","ix":3,"v":{"a":1,"k":[{"t":149,"s":[0],"h":1},{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[8]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[38]},{"i":{"x":[0.002],"y":[1]},"o":{"x":[0.119],"y":[0]},"t":162,"s":[48]},{"t":217,"s":[64]}]}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0005","ix":5,"v":0},{"ty":6,"nm":"Rounding","mn":"Pseudo/0.20784385308943532-0006","ix":6,"v":0},{"ty":7,"nm":"Same for all corners","mn":"Pseudo/0.20784385308943532-0007","ix":7,"v":{"a":0,"k":1}},{"ty":0,"nm":"All corners","mn":"Pseudo/0.20784385308943532-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.02],"y":[1]},"o":{"x":[0.365],"y":[0]},"t":150,"s":[80]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.336],"y":[0]},"t":159,"s":[24]},{"t":162,"s":[80]}]}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0013","ix":13,"v":0},{"ty":6,"nm":"Alignment","mn":"Pseudo/0.20784385308943532-0014","ix":14,"v":0},{"ty":0,"nm":"X Anchor %","mn":"Pseudo/0.20784385308943532-0015","ix":15,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":[{"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[0,-24],[0,-24],[0,-24],[0,-24],[0,24],[0,24],[0,24],[0,24]],"c":true}],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.208,0],[0,0],[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208]],"o":[[0,-2.208],[0,0],[2.208,0],[0,0],[0,2.208],[0,0],[-2.208,0],[0,0]],"v":[[-8,-20],[-4,-24],[-4,-24],[0,-20],[0,20],[-4,24],[-4,24],[-8,20]],"c":true}],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-2.594,0],[0,0],[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594]],"o":[[0,-2.594],[0,0],[2.594,0],[0,0],[0,2.594],[0,0],[-2.594,0],[0,0]],"v":[[-9.401,-19.3],[-4.7,-24],[-4.7,-24],[0,-19.3],[0,19.3],[-4.7,24],[-4.7,24],[-9.401,19.3]],"c":true}],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-4.958,0],[0,0],[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958]],"o":[[0,-4.958],[0,0],[4.958,0],[0,0],[0,4.958],[0,0],[-4.958,0],[0,0]],"v":[[-17.967,-15.017],[-8.983,-24],[-8.983,-24],[0,-15.017],[0,15.017],[-8.983,24],[-8.983,24],[-17.967,15.017]],"c":true}],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-7.632,0],[0,0],[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632]],"o":[[0,-7.632],[0,0],[7.632,0],[0,0],[0,7.632],[0,0],[-7.632,0],[0,0]],"v":[[-27.659,-10.171],[-13.829,-24],[-13.829,-24],[0,-10.171],[0,10.171],[-13.829,24],[-13.829,24],[-27.659,10.171]],"c":true}],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-8.91,0],[0,0],[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91]],"o":[[0,-8.91],[0,0],[8.91,0],[0,0],[0,8.91],[0,0],[-8.91,0],[0,0]],"v":[[-32.287,-7.856],[-16.144,-24],[-16.144,-24],[0,-7.856],[0,7.856],[-16.144,24],[-16.144,24],[-32.287,7.856]],"c":true}],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-9.63,0],[0,0],[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63]],"o":[[0,-9.63],[0,0],[9.63,0],[0,0],[0,9.63],[0,0],[-9.63,0],[0,0]],"v":[[-34.898,-6.551],[-17.449,-24],[-17.449,-24],[0,-6.551],[0,6.551],[-17.449,24],[-17.449,24],[-34.898,6.551]],"c":true}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.063,0],[0,0],[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063]],"o":[[0,-10.063],[0,0],[10.063,0],[0,0],[0,10.063],[0,0],[-10.063,0],[0,0]],"v":[[-36.467,-5.766],[-18.234,-24],[-18.234,-24],[0,-5.766],[0,5.766],[-18.234,24],[-18.234,24],[-36.467,5.766]],"c":true}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.317,0],[0,0],[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317]],"o":[[0,-10.317],[0,0],[10.317,0],[0,0],[0,10.317],[0,0],[-10.317,0],[0,0]],"v":[[-37.388,-5.306],[-18.694,-24],[-18.694,-24],[0,-5.306],[0,5.306],[-18.694,24],[-18.694,24],[-37.388,5.306]],"c":true}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.448,0],[0,0],[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448]],"o":[[0,-10.448],[0,0],[10.448,0],[0,0],[0,10.448],[0,0],[-10.448,0],[0,0]],"v":[[-37.861,-5.07],[-18.93,-24],[-18.93,-24],[0,-5.07],[0,5.07],[-18.93,24],[-18.93,24],[-37.861,5.07]],"c":true}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-10.486,0],[0,0],[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486]],"o":[[0,-10.486],[0,0],[10.486,0],[0,0],[0,10.486],[0,0],[-10.486,0],[0,0]],"v":[[-38,-5],[-19,-24],[-19,-24],[0,-5],[0,5],[-19,24],[-19,24],[-38,5]],"c":true}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-12.154,0],[0,0],[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154]],"o":[[0,-12.154],[0,0],[12.154,0],[0,0],[0,12.154],[0,0],[-12.154,0],[0,0]],"v":[[-44.045,-1.977],[-22.023,-24],[-22.023,-24],[0,-1.977],[0,1.977],[-22.023,24],[-22.023,24],[-44.045,1.977]],"c":true}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.079,0],[0,0],[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079]],"o":[[0,-13.079],[0,0],[13.079,0],[0,0],[0,13.079],[0,0],[-13.079,0],[0,0]],"v":[[-47.396,-0.302],[-23.698,-24],[-23.698,-24],[0,-0.302],[0,0.302],[-23.698,24],[-23.698,24],[-47.396,0.302]],"c":true}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48,0],[-24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24,24],[-48,0]],"c":true}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.149,0],[-24.149,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.149,24],[-48.149,0]],"c":true}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-48.698,0],[-24.698,-24],[-24,-24],[0,0],[0,0],[-24,24],[-24.698,24],[-48.698,0]],"c":true}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-49.714,0],[-25.714,-24],[-24,-24],[0,0],[0,0],[-24,24],[-25.714,24],[-49.714,0]],"c":true}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-50.973,0],[-26.973,-24],[-24,-24],[0,0],[0,0],[-24,24],[-26.973,24],[-50.973,0]],"c":true}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-52.19,0],[-28.19,-24],[-24,-24],[0,0],[0,0],[-24,24],[-28.19,24],[-52.19,0]],"c":true}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-53.268,0],[-29.268,-24],[-24,-24],[0,0],[0,0],[-24,24],[-29.268,24],[-53.268,0]],"c":true}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-54.206,0],[-30.206,-24],[-24,-24],[0,0],[0,0],[-24,24],[-30.206,24],[-54.206,0]],"c":true}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.028,0],[-31.028,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.028,24],[-55.028,0]],"c":true}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-55.755,0],[-31.755,-24],[-24,-24],[0,0],[0,0],[-24,24],[-31.755,24],[-55.755,0]],"c":true}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.405,0],[-32.405,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.405,24],[-56.405,0]],"c":true}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-56.99,0],[-32.99,-24],[-24,-24],[0,0],[0,0],[-24,24],[-32.99,24],[-56.99,0]],"c":true}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-57.522,0],[-33.522,-24],[-24,-24],[0,0],[0,0],[-24,24],[-33.522,24],[-57.522,0]],"c":true}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.006,0],[-34.006,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.006,24],[-58.006,0]],"c":true}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.451,0],[-34.451,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.451,24],[-58.451,0]],"c":true}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-58.861,0],[-34.861,-24],[-24,-24],[0,0],[0,0],[-24,24],[-34.861,24],[-58.861,0]],"c":true}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.24,0],[-35.24,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.24,24],[-59.24,0]],"c":true}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.591,0],[-35.591,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.591,24],[-59.591,0]],"c":true}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-59.917,0],[-35.917,-24],[-24,-24],[0,0],[0,0],[-24,24],[-35.917,24],[-59.917,0]],"c":true}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.221,0],[-36.221,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.221,24],[-60.221,0]],"c":true}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.504,0],[-36.504,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.504,24],[-60.504,0]],"c":true}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-60.769,0],[-36.769,-24],[-24,-24],[0,0],[0,0],[-24,24],[-36.769,24],[-60.769,0]],"c":true}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.017,0],[-37.017,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.017,24],[-61.017,0]],"c":true}],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.248,0],[-37.248,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.248,24],[-61.248,0]],"c":true}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.465,0],[-37.465,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.465,24],[-61.465,0]],"c":true}],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.669,0],[-37.669,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.669,24],[-61.669,0]],"c":true}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-61.859,0],[-37.859,-24],[-24,-24],[0,0],[0,0],[-24,24],[-37.859,24],[-61.859,0]],"c":true}],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.038,0],[-38.038,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.038,24],[-62.038,0]],"c":true}],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.205,0],[-38.205,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.205,24],[-62.205,0]],"c":true}],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.362,0],[-38.362,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.362,24],[-62.362,0]],"c":true}],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.509,0],[-38.509,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.509,24],[-62.509,0]],"c":true}],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.647,0],[-38.647,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.647,24],[-62.647,0]],"c":true}],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.776,0],[-38.776,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.776,24],[-62.776,0]],"c":true}],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-62.896,0],[-38.896,-24],[-24,-24],[0,0],[0,0],[-24,24],[-38.896,24],[-62.896,0]],"c":true}],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.008,0],[-39.008,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.008,24],[-63.008,0]],"c":true}],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.113,0],[-39.113,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.113,24],[-63.113,0]],"c":true}],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.21,0],[-39.21,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.21,24],[-63.21,0]],"c":true}],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.3,0],[-39.3,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.3,24],[-63.3,0]],"c":true}],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.384,0],[-39.384,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.384,24],[-63.384,0]],"c":true}],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.461,0],[-39.461,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.461,24],[-63.461,0]],"c":true}],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.532,0],[-39.532,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.532,24],[-63.532,0]],"c":true}],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.597,0],[-39.597,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.597,24],[-63.597,0]],"c":true}],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.657,0],[-39.657,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.657,24],[-63.657,0]],"c":true}],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.711,0],[-39.711,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.711,24],[-63.711,0]],"c":true}],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.76,0],[-39.76,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.76,24],[-63.76,0]],"c":true}],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.804,0],[-39.804,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.804,24],[-63.804,0]],"c":true}],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.843,0],[-39.843,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.843,24],[-63.843,0]],"c":true}],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.877,0],[-39.877,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.877,24],[-63.877,0]],"c":true}],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.907,0],[-39.907,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.907,24],[-63.907,0]],"c":true}],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.932,0],[-39.932,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.932,24],[-63.932,0]],"c":true}],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[-13.246,0],[0,0],[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246]],"o":[[0,-13.246],[0,0],[13.246,0],[0,0],[0,13.246],[0,0],[-13.246,0],[0,0]],"v":[[-63.971,0],[-39.971,-24],[-24,-24],[0,0],[0,0],[-24,24],[-39.971,24],[-63.971,0]],"c":true}],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"k":[{"s":[0,0],"t":25,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":498,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"IndieCorners Shape","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":249,"s":[100]},{"t":255,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,0,0],"t":123,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0,0],"t":124,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.001,0,0],"t":125,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.005,0,0],"t":126,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.013,0,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.029,0,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.054,0,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.089,0,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.134,0,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.193,0,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.267,0,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.358,0,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.466,0,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.593,0,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.739,0,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-0.903,0,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.054,0,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.22,0,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.403,0,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.602,0,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-1.821,0,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.059,0,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.319,0,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.601,0,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-2.909,0,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.242,0,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.604,0,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-3.998,0,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.427,0,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-4.897,0,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.407,0,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-5.965,0,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-6.576,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.246,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-7.983,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-8.8,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-9.701,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-10.699,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-11.808,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-13.041,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-14.414,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-15.945,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-17.621,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-19.429,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-21.324,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-23.241,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-25.111,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-26.859,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-28.457,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-29.897,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-31.185,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.333,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-33.36,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-34.272,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.088,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-35.82,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-36.479,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.076,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-37.613,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.099,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.538,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-38.937,0,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.299,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.629,0,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-39.927,0,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.198,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.442,0,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.663,0,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-40.862,0,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.041,0,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.2,0,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.342,0,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.467,0,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.577,0,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.672,0,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.754,0,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.822,0,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.879,0,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.925,0,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-41.961,0,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Super Slider","np":3,"mn":"ADBE Slider Control","ix":1,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":121,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":138,"s":[17.5]},{"t":205,"s":[100]}]}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"right circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":62,"s":[-41,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":0.56},"o":{"x":0.44,"y":0.44},"t":72,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"i":{"x":0.56,"y":1},"o":{"x":0.44,"y":0},"t":247,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":257,"s":[-41,0]}]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"left circle","bm":0,"hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":62,"s":[36,36]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":72,"s":[28,28]},{"i":{"x":[0.56,0.56],"y":[1,1]},"o":{"x":[0.44,0.44],"y":[0,0]},"t":247,"s":[28,28]},{"t":257,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"size","bm":0,"hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[276.737,197.5,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5,0],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5,0],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.809,197.5,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.805,197.5,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.726,197.5,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.159,197.5,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[256.3,197.5,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.431,197.5,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.009,197.5,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.087,197.5,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.496,197.5,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.536,197.5,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.339,197.5,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.98,197.5,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.502,197.5,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.933,197.5,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.295,197.5,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.599,197.5,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.857,197.5,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.075,197.5,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.259,197.5,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.415,197.5,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.655,197.5,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.926,197.5,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[0,0,0]},"s":{"k":[{"s":[99.914,99.914,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.848,99.848,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.751,99.751,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.685,99.685,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.605,99.605,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.507,99.507,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.387,99.387,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.239,99.239,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.056,99.056,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.829,98.829,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.542,98.542,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.174,98.174,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.686,97.686,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97,97,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.071,96.071,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.025,95.025,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.878,93.878,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.678,92.678,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.495,91.495,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.39,90.39,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[89.393,89.393,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.508,88.508,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.729,87.729,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.041,87.041,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[86.43,86.43,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.886,85.886,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.397,85.397,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.956,84.956,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.555,84.555,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.191,84.191,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.857,83.857,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.552,83.552,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.271,83.271,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.011,83.011,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.771,82.771,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.549,82.549,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.342,82.342,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.151,82.151,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.973,81.973,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.807,81.807,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.653,81.653,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.51,81.51,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.376,81.376,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.251,81.251,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.135,81.135,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.027,81.027,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.926,80.926,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.833,80.833,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.746,80.746,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.665,80.665,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.591,80.591,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.522,80.522,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.458,80.458,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.4,80.4,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.346,80.346,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.298,80.298,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.253,80.253,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.176,80.176,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.115,80.115,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.049,80.049,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.179,80.179,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.757,80.757,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[81.87,81.87,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[83.86,83.86,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88,88,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.714,92.714,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.789,94.789,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.992,95.992,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.809,96.809,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.412,97.412,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.878,97.878,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.249,98.249,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.553,98.553,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.803,98.803,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.012,99.012,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.188,99.188,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.337,99.337,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.464,99.464,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.57,99.57,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.661,99.661,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.737,99.737,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.8,99.8,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.896,99.896,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.99,99.99,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":256,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":7}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[276.737,197.5],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.57,197.5],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.319,197.5],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.15,197.5],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.942,197.5],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.687,197.5],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.37,197.5],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.978,197.5],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.484,197.5],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.85,197.5],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.008,197.5],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.825,197.5],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.222,197.5],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[268.416,197.5],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[266.436,197.5],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[264.37,197.5],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.33,197.5],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[260.423,197.5],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[258.703,197.5],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.178,197.5],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[255.833,197.5],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[254.646,197.5],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[253.594,197.5],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252.657,197.5],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.814,197.5],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.052,197.5],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[250.36,197.5],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.73,197.5],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[249.154,197.5],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.627,197.5],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[248.142,197.5],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.694,197.5],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[247.28,197.5],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.897,197.5],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.541,197.5],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[246.211,197.5],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.904,197.5],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.619,197.5],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.353,197.5],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.107,197.5],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.876,197.5],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.661,197.5],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.461,197.5],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.274,197.5],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[244.099,197.5],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.937,197.5],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.787,197.5],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.648,197.5],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.52,197.5],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.291,197.5],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.098,197.5],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.866,197.5],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.655,197.5],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[243.159,197.525],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[245.792,197.842],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[251.233,199.672],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[257.542,204.005],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[262.216,208.989],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[265.399,213.457],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[267.667,217.355],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[269.364,220.773],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[270.685,223.812],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[271.743,226.538],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[272.606,228.991],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.321,231.197],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[273.92,233.179],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.427,234.953],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[274.858,236.532],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.225,237.926],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.539,239.152],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[275.808,240.219],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.038,241.138],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.233,241.918],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.398,242.568],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.537,243.099],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.653,243.517],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.824,243.574],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.884,243.254],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.928,242.802],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.959,242.269],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.976,241.685],"t":279,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,241.075],"t":280,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,240.497],"t":281,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.98],"t":282,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.538],"t":283,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.181],"t":284,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,238.917],"t":285,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.065],"t":293,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.265],"t":295,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.455],"t":297,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[276.98,239.685],"t":300,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.15,239.729],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.715,239.199],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.839,238.218],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.95,236.594],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[286.015,234.04],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[288.407,226.983],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[283.954,217.108],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[282.005,212.156],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.975,209.156],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[280.314,207.064],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.834,205.487],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.461,204.24],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[279.159,203.226],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.905,202.385],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.691,201.676],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.503,201.072],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.339,200.553],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.193,200.105],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[278.061,199.716],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.941,199.376],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.831,199.079],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.729,198.82],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.636,198.594],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.551,198.398],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.472,198.228],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.399,198.082],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.333,197.956],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.209,197.759],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277.063,197.577],"t":412,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"matte","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":250,"s":[0,0,0],"to":[29.688,0.625,0],"ti":[0,0,0]},{"t":280,"s":[43.1,53,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[43.1,53,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":386,"s":[25.86,31.8,0],"to":[0,0,0],"ti":[0,0,0]},{"t":416,"s":[0,0,0]}]},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":255,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.236,"y":0},"t":273,"s":[0,-6,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":287,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":307,"s":[0,0,0]}]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.2,0.2],"y":[0,0]},"t":250,"s":[504,315]},{"t":280,"s":[30,30],"h":1},{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":380,"s":[30,30]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":386,"s":[219.6,144]},{"t":416,"s":[504,315]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":250,"s":[28]},{"t":280,"s":[30],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[30]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":386,"s":[29.2]},{"t":416,"s":[28]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":0,"nm":"Back_LofiApp","parent":9,"tt":1,"tp":9,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":25,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":498,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Back_LeftDismiss","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":426,"st":-25,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Back_RightDismiss","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":426,"op":902,"st":401,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_back_success.json b/packages/SystemUI/res/raw/trackpad_back_success.json new file mode 100644 index 000000000000..56b6ff17d1f6 --- /dev/null +++ b/packages/SystemUI/res/raw/trackpad_back_success.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadBack_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}]},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[95.049,95.049,100]}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}]},"p":{"a":0,"k":[81,127,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-0.289},"p":{"a":0,"k":[14.364,-33.591,0]},"a":{"a":0,"k":[-0.125,0,0]},"s":{"a":0,"k":[104.744,104.744,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":11},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[95,95,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":88},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"k":[{"s":[0.208,0.302,0.184,1],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.208,0.302,0.184,1],"t":43,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[339.937,151.75,0]},"a":{"a":0,"k":[339.937,151.75,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[1.021,-1.766],[0,0],[-2.043,0],[0,0],[1.022,1.767]],"o":[[-1.021,-1.766],[0,0],[-1.022,1.767],[0,0],[2.043,0],[0,0]],"v":[[2.297,-7.675],[-2.297,-7.675],[-9.64,5.025],[-7.343,9],[7.343,9],[9.64,5.025]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Rectangle","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[334,279]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Text field","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Sent","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Received","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[334,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":16},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,171.125,0]},"a":{"a":0,"k":[82,171.125,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 2","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82,140,0]},"a":{"a":0,"k":[82,140.938,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":39.375},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Search","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"header","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":12},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Message","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[82,171]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"block","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":200},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Avatar","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"circle 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 3","np":8,"mn":"ADBE Drop Shadow","ix":3,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 4","np":8,"mn":"ADBE Drop Shadow","ix":4,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 5","np":8,"mn":"ADBE Drop Shadow","ix":5,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 6","np":8,"mn":"ADBE Drop Shadow","ix":6,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 7","np":8,"mn":"ADBE Drop Shadow","ix":7,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 8","np":8,"mn":"ADBE Drop Shadow","ix":8,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 9","np":8,"mn":"ADBE Drop Shadow","ix":9,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 10","np":8,"mn":"ADBE Drop Shadow","ix":10,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 11","np":8,"mn":"ADBE Drop Shadow","ix":11,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 12","np":8,"mn":"ADBE Drop Shadow","ix":12,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 13","np":8,"mn":"ADBE Drop Shadow","ix":13,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.039999999106]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":10.2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0.394}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1.181}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]},{"ty":25,"nm":"Drop Shadow 14","np":8,"mn":"ADBE Drop Shadow","ix":14,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,0.029999999329]}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":7.65}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":2.362}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0}}]}],"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215686275,0.125490196078,0.027450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":3,"nm":"Scale Up","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":250,"s":[85,85,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":256,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":286,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[100,100,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[96,96,100]},{"t":416,"s":[90,90,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,459,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":18},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Frame 1321317559","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadBack_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,198.5,0]},"a":{"a":0,"k":[95,95,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":190,"h":190,"ip":6,"op":50,"st":6,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onTertiaryFixed","cl":"onTertiaryFixed","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":3}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[0,0],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Back_LofiApp","parent":4,"tt":1,"tp":4,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":250,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":280,"s":[10,10,100]},{"i":{"x":[0.8,0.8,0.8],"y":[0.15,0.15,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":380,"s":[10,10,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.7,0.7,0]},"t":386,"s":[46,46,100]},{"t":416,"s":[100,100,100]}]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"behindApp","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":253,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":386,"s":[0]},{"t":397,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index eda7bb0e7f6d..e45934825474 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1730,9 +1730,15 @@ <!-- Ongoing activity chip --> <dimen name="ongoing_activity_chip_side_padding">12dp</dimen> + <!-- The start padding to use on the activity chip if the icon comes from the notification's smallIcon field (the small notif icons embed their own padding, so the chip itself can have less padding) --> + <dimen name="ongoing_activity_chip_side_padding_with_notif_icon">6dp</dimen> <dimen name="ongoing_activity_chip_icon_size">16dp</dimen> + <!-- The size to use for the icon in the ongoing activity chip if the icon comes from the notification's smallIcon field --> + <dimen name="ongoing_activity_chip_notif_icon_size">22dp</dimen> + <!-- The padding between the icon and the text. --> <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen> + <dimen name="ongoing_activity_chip_text_end_padding_with_notif_icon">6dp</dimen> <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen> <!-- Status bar user chip --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 36912acbbdb9..c428705dab78 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1386,9 +1386,13 @@ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item> </style> - <style name="TextAppearance.InternetDialog.Active"/> + <style name="TextAppearance.InternetDialog.Active"> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> + </style> - <style name="TextAppearance.InternetDialog.Secondary.Active"/> + <style name="TextAppearance.InternetDialog.Secondary.Active"> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> + </style> <style name="FgsManagerDialogTitle"> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> @@ -1424,17 +1428,32 @@ <item name="android:orientation">horizontal</item> <item name="android:focusable">true</item> <item name="android:clickable">true</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> + </style> + + <style name="BluetoothTileDialog.Device.Active"> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> </style> <style name="BluetoothTileDialog.DeviceName"> <item name="android:textSize">14sp</item> <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> </style> <style name="BluetoothTileDialog.DeviceSummary"> <item name="android:ellipsize">end</item> <item name="android:maxLines">2</item> <item name="android:textAppearance">@style/TextAppearance.Dialog.Body.Message</item> + <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item> + </style> + + <style name="BluetoothTileDialog.DeviceName.Active"> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> + </style> + + <style name="BluetoothTileDialog.DeviceSummary.Active"> + <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item> </style> <style name="BroadcastDialog"> diff --git a/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt b/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt new file mode 100644 index 000000000000..dc2d931aad41 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/activatable/Activatable.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.activatable + +/** Defines interface for classes that can be activated to do coroutine work. */ +interface Activatable { + + /** + * Activates this object. + * + * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep + * its state fresh and/or perform side-effects. + * + * The method suspends and doesn't return until all work required by the object is finished. In + * most cases, it's expected for the work to remain ongoing forever so this method will forever + * suspend its caller until the coroutine that called it is canceled. + * + * Implementations could follow this pattern: + * ```kotlin + * override suspend fun activate() { + * coroutineScope { + * launch { ... } + * launch { ... } + * launch { ... } + * } + * } + * ``` + * + * **Must be invoked** by the owner of the object when the object is to become active. + * Similarly, the work must be canceled by the owner when the objects is to be deactivated. + * + * One way to have a parent call this would be by using a `LaunchedEffect` in Compose: + * ```kotlin + * @Composable + * fun MyUi(activatable: Activatable) { + * LaunchedEffect(activatable) { + * activatable.activate() + * } + * } + * ``` + */ + suspend fun activate() +} diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt index 911145b62661..f5b9a050f33e 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt @@ -37,6 +37,7 @@ import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.android.internal.R as InternalR import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R @@ -367,6 +368,7 @@ internal constructor( private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name) private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary) private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon) + private val iconGear = view.requireViewById<ImageView>(R.id.gear_icon_image) private val gearView = view.requireViewById<View>(R.id.gear_icon) internal fun bind( @@ -380,6 +382,36 @@ internal constructor( mutableDeviceItemClick.tryEmit(item) uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED) } + + // updating icon colors + val tintColor = + com.android.settingslib.Utils.getColorAttr( + context, + if (item.isActive) InternalR.attr.materialColorOnPrimaryContainer + else InternalR.attr.materialColorOnSurface + ) + .defaultColor + + // update icons + iconView.apply { + item.iconWithDescription?.let { + setImageDrawable(it.first.apply { mutate()?.setTint(tintColor) }) + contentDescription = it.second + } + } + + iconGear.apply { drawable?.let { it.mutate()?.setTint(tintColor) } } + + // update text styles + nameView.setTextAppearance( + if (item.isActive) R.style.BluetoothTileDialog_DeviceName_Active + else R.style.BluetoothTileDialog_DeviceName + ) + summaryView.setTextAppearance( + if (item.isActive) R.style.BluetoothTileDialog_DeviceSummary_Active + else R.style.BluetoothTileDialog_DeviceSummary + ) + accessibilityDelegate = object : AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( @@ -398,12 +430,7 @@ internal constructor( } nameView.text = item.deviceName summaryView.text = item.connectionSummary - iconView.apply { - item.iconWithDescription?.let { - setImageDrawable(it.first) - contentDescription = it.second - } - } + gearView.setOnClickListener { deviceItemOnClickCallback.onDeviceItemGearClicked(item, it) } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt index 0ea98d14bca3..a78130f1b041 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt @@ -52,4 +52,5 @@ data class DeviceItem( val background: Int? = null, var isEnabled: Boolean = true, var actionAccessibilityLabel: String = "", + var isActive: Boolean = false ) diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt index 51b228026b03..d7893dbb0f90 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt @@ -55,7 +55,8 @@ internal abstract class DeviceItemFactory { type: DeviceItemType, connectionSummary: String, background: Int, - actionAccessibilityLabel: String + actionAccessibilityLabel: String, + isActive: Boolean ): DeviceItem { return DeviceItem( type = type, @@ -68,7 +69,8 @@ internal abstract class DeviceItemFactory { }, background = background, isEnabled = !cachedDevice.isBusy, - actionAccessibilityLabel = actionAccessibilityLabel + actionAccessibilityLabel = actionAccessibilityLabel, + isActive = isActive ) } } @@ -91,7 +93,8 @@ internal open class ActiveMediaDeviceItemFactory : DeviceItemFactory() { DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE, cachedDevice.connectionSummary ?: "", backgroundOn, - context.getString(actionAccessibilityLabelDisconnect) + context.getString(actionAccessibilityLabelDisconnect), + isActive = true ) } } @@ -116,7 +119,8 @@ internal class AudioSharingMediaDeviceItemFactory( cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } ?: context.getString(audioSharing), if (cachedDevice.isBusy) backgroundOffBusy else backgroundOn, - "" + "", + isActive = !cachedDevice.isBusy ) } } @@ -150,7 +154,8 @@ internal open class AvailableMediaDeviceItemFactory : DeviceItemFactory() { cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } ?: context.getString(connected), if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff, - context.getString(actionAccessibilityLabelActivate) + context.getString(actionAccessibilityLabelActivate), + isActive = false ) } } @@ -188,7 +193,8 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } ?: context.getString(connected), if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff, - context.getString(actionAccessibilityLabelDisconnect) + context.getString(actionAccessibilityLabelDisconnect), + isActive = false ) } } @@ -216,7 +222,8 @@ internal open class SavedDeviceItemFactory : DeviceItemFactory() { cachedDevice.connectionSummary.takeUnless { it.isNullOrEmpty() } ?: context.getString(saved), if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff, - context.getString(actionAccessibilityLabelActivate) + context.getString(actionAccessibilityLabelActivate), + isActive = false ) } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 40a141dcec77..e2089bbb4504 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.input.key.KeyEvent import androidx.compose.ui.input.key.type import androidx.core.graphics.drawable.toBitmap import com.android.compose.animation.scene.Back -import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction @@ -43,7 +42,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor -import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.user.ui.viewmodel.UserActionViewModel import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel @@ -67,7 +65,9 @@ import kotlinx.coroutines.launch /** Holds UI state and handles user input on bouncer UIs. */ class BouncerViewModel( @Application private val applicationContext: Context, - @Application private val applicationScope: CoroutineScope, + @Deprecated("TODO(b/354270224): remove this. Injecting CoroutineScope to view-models is banned") + @Application + private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, private val bouncerInteractor: BouncerInteractor, private val inputMethodInteractor: InputMethodInteractor, @@ -91,14 +91,13 @@ class BouncerViewModel( initialValue = null, ) - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - bouncerInteractor.dismissDestination - .map(::destinationSceneMap) - .stateIn( - applicationScope, - SharingStarted.WhileSubscribed(), - initialValue = destinationSceneMap(Scenes.Lockscreen), + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + bouncerInteractor.dismissDestination.map { prevScene -> + mapOf( + Back to UserActionResult(prevScene), + Swipe(SwipeDirection.Down) to UserActionResult(prevScene), ) + } val message: BouncerMessageViewModel = bouncerMessageViewModel @@ -328,8 +327,7 @@ class BouncerViewModel( { message }, failedAttempts, remainingAttempts, - ) - ?: message + ) ?: message } else { message } @@ -346,8 +344,7 @@ class BouncerViewModel( .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE, { message }, failedAttempts, - ) - ?: message + ) ?: message } else { message } @@ -375,12 +372,6 @@ class BouncerViewModel( } } - private fun destinationSceneMap(prevScene: SceneKey) = - mapOf( - Back to UserActionResult(prevScene), - Swipe(SwipeDirection.Down) to UserActionResult(prevScene), - ) - /** * Notifies that a key event has occurred. * @@ -390,8 +381,7 @@ class BouncerViewModel( return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent( keyEvent.type, keyEvent.nativeKeyEvent.keyCode - ) - ?: false + ) ?: false } data class DialogViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index bd0e72901b3d..04c6fa944943 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -301,8 +301,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_SHOWN_EXPANDED); setExpandedView(this::animateIn); } - mView.announceForAccessibility( - getAccessibilityAnnouncement(mClipboardModel.getType())); + mWindow.withWindowAttached(() -> mView.announceForAccessibility( + getAccessibilityAnnouncement(mClipboardModel.getType()))); } else if (!mIsMinimized) { setExpandedView(() -> { }); @@ -320,8 +320,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv setExpandedView(); } animateIn(); - mView.announceForAccessibility( - getAccessibilityAnnouncement(mClipboardModel.getType())); + mWindow.withWindowAttached(() -> mView.announceForAccessibility( + getAccessibilityAnnouncement(mClipboardModel.getType()))); } else if (!mIsMinimized) { setExpandedView(); } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 6b7712d9364e..b7c02ea91a6b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -26,6 +26,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -103,11 +104,14 @@ constructor( .mapLatest(::determineSceneAfterTransition) .filterNotNull() .onEach { (nextScene, nextTransition) -> - if (!communalSceneInteractor.isLaunchingWidget.value) { - // When launching a widget, we don't want to animate the scene change or the - // Communal Hub will reveal the wallpaper even though it shouldn't. Instead - // we snap to the new scene as part of the launch animation, once the - // activity launch is done, so we don't change scene here. + // When launching a widget, we don't want to animate the scene change or the + // Communal Hub will reveal the wallpaper even though it shouldn't. Instead we + // snap to the new scene as part of the launch animation, once the activity + // launch is done, so we don't change scene here. + val delaySceneTransition = + communalSceneInteractor.editModeState.value == EditModeState.STARTING || + communalSceneInteractor.isLaunchingWidget.value + if (!delaySceneTransition) { communalSceneInteractor.changeScene(nextScene, nextTransition) } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt index 81feb441cfbf..2352841fdde9 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt @@ -19,14 +19,16 @@ package com.android.systemui.communal.log import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.logging.UiEventLogger import com.android.systemui.CoreStartable -import com.android.systemui.communal.domain.interactor.CommunalInteractor +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filterNotNull @@ -40,12 +42,13 @@ class CommunalLoggerStartable @Inject constructor( @Background private val backgroundScope: CoroutineScope, - private val communalInteractor: CommunalInteractor, + private val communalSceneInteractor: CommunalSceneInteractor, + private val keyguardInteractor: KeyguardInteractor, private val uiEventLogger: UiEventLogger, ) : CoreStartable { override fun start() { - communalInteractor.transitionState + communalSceneInteractor.transitionState .map { state -> when { state.isOnCommunal() -> CommunalUiEvent.COMMUNAL_HUB_SHOWN @@ -60,22 +63,46 @@ constructor( .onEach { uiEvent -> uiEventLogger.log(uiEvent) } .launchIn(backgroundScope) - communalInteractor.transitionState + communalSceneInteractor.transitionState .pairwise() - .map { (old, new) -> + .combine(keyguardInteractor.isDreamingWithOverlay) { (old, new), isDreaming -> when { new.isOnCommunal() && old.isSwipingToCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH + if (isDreaming) { + CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_FINISH + } new.isOnCommunal() && old.isSwipingFromCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL + if (isDreaming) { + CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_CANCEL + } new.isNotOnCommunal() && old.isSwipingFromCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH + if (isDreaming) { + CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_FINISH + } new.isNotOnCommunal() && old.isSwipingToCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL + if (isDreaming) { + CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_CANCEL + } new.isSwipingToCommunal() && old.isNotOnCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START + if (isDreaming) { + CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_SWIPE_START + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_ENTER_START + } new.isSwipingFromCommunal() && old.isOnCommunal() -> - CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START + if (isDreaming) { + CommunalUiEvent.COMMUNAL_HUB_TO_DREAM_SWIPE_START + } else { + CommunalUiEvent.COMMUNAL_HUB_SWIPE_TO_EXIT_START + } else -> null } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt index 6e345fe27eea..9ce8cf72983a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt @@ -56,6 +56,19 @@ constructor( ) } + /** Logs a tap widget event for metrics. No-op if widget is not loggable. */ + fun logTapWidget(componentName: String, rank: Int) { + if (!componentName.isLoggable()) { + return + } + + statsLogProxy.writeCommunalHubWidgetEventReported( + SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP, + componentName, + rank, + ) + } + /** Logs loggable widgets and the total widget count as a [StatsEvent]. */ fun logWidgetsSnapshot( statsEvents: MutableList<StatsEvent>, diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt index 4ab56cc34628..4711d8833df2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalUiEvent.kt @@ -54,14 +54,20 @@ enum class CommunalUiEvent(private val id: Int) : UiEventEnum { COMMUNAL_HUB_SWIPE_UP_TO_BOUNCER(1573), @UiEvent(doc = "User performs a swipe down gesture from top to enter shade") COMMUNAL_HUB_SWIPE_DOWN_TO_SHADE(1574), - @UiEvent(doc = "User performs a tap gesture on the UMO in Communal Hub") - COMMUNAL_HUB_UMO_TAP(1858), - @UiEvent( - doc = - "A transition from dream to Communal Hub starts. This can be triggered by a tap on " + - "the dream." - ) - FROM_DREAM_TO_COMMUNAL_HUB_TRANSITION_START(1859); + @UiEvent(doc = "User starts the swipe gesture to enter the Communal Hub from Dream") + DREAM_TO_COMMUNAL_HUB_SWIPE_START(1860), + @UiEvent(doc = "User finishes the swipe gesture to enter the Communal Hub from Dream") + DREAM_TO_COMMUNAL_HUB_SWIPE_FINISH(1861), + @UiEvent(doc = "User cancels the swipe gesture to enter the Communal Hub from Dream") + DREAM_TO_COMMUNAL_HUB_SWIPE_CANCEL(1862), + @UiEvent(doc = "User starts the swipe gesture to exit the Communal Hub to go to Dream") + COMMUNAL_HUB_TO_DREAM_SWIPE_START(1863), + @UiEvent(doc = "User finishes the swipe gesture to exit the Communal Hub to go to Dream") + COMMUNAL_HUB_TO_DREAM_SWIPE_FINISH(1864), + @UiEvent(doc = "User cancels the swipe gesture to exit the Communal Hub to go to Dream") + COMMUNAL_HUB_TO_DREAM_SWIPE_CANCEL(1865), + @UiEvent(doc = "A transition from Dream to Communal Hub starts due to dream awakening") + DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START(1866); override fun getId(): Int { return id diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 623e702a85fe..b8601ec15132 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -139,6 +139,12 @@ abstract class BaseCommunalViewModel( priority: Int, ) {} + /** Called as the UI detects a tap event on the widget. */ + open fun onTapWidget( + componentName: ComponentName, + priority: Int, + ) {} + /** * Called as the UI requests reordering widgets. * diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt index e1408a065a37..bbd8596a76bd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.communal.ui.viewmodel import android.graphics.Color import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor +import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.util.CommunalColors import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -58,9 +59,17 @@ constructor( dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel, glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel, communalInteractor: CommunalInteractor, - communalSceneInteractor: CommunalSceneInteractor, + private val communalSceneInteractor: CommunalSceneInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor ) { + /** + * Snaps to [CommunalScenes.Communal], showing the glanceable hub immediately without any + * transition. + */ + fun snapToCommunal() { + communalSceneInteractor.snapToScene(CommunalScenes.Communal) + } + // Show UMO on glanceable hub immediately on transition into glanceable hub private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> = keyguardTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 1e087f789faa..3fc8b096a48b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal.ui.viewmodel +import android.content.ComponentName import android.content.res.Resources import android.os.Bundle import android.view.View @@ -25,6 +26,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.log.CommunalMetricsLogger import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -92,6 +94,7 @@ constructor( private val shadeInteractor: ShadeInteractor, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, @CommunalLog logBuffer: LogBuffer, + private val metricsLogger: CommunalMetricsLogger, ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) { private val _isMediaHostVisible = @@ -260,6 +263,10 @@ constructor( } } + override fun onTapWidget(componentName: ComponentName, priority: Int) { + metricsLogger.logTapWidget(componentName.flattenToString(), priority) + } + fun onClick() { keyguardIndicationController.showActionToUnlock() } diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 398576935eed..03ef17b6ec5b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -128,7 +128,7 @@ constructor( Box( modifier = Modifier.fillMaxSize() - .background(LocalAndroidColorScheme.current.onSecondaryFixed), + .background(LocalAndroidColorScheme.current.surfaceDim), ) { CommunalHub( viewModel = communalViewModel, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 2fbb75ec720a..c2e1e33f5318 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -29,7 +29,6 @@ import com.android.systemui.screenshot.scroll.LongScreenshotActivity; import com.android.systemui.sensorprivacy.SensorUseStartedActivity; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity; -import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity; import com.android.systemui.tuner.TunerActivity; import com.android.systemui.usb.UsbAccessoryUriActivity; import com.android.systemui.usb.UsbConfirmActivity; @@ -157,10 +156,4 @@ public abstract class DefaultActivityBinder { @ClassKey(SwitchToManagedProfileForCallActivity.class) public abstract Activity bindSwitchToManagedProfileForCallActivity( SwitchToManagedProfileForCallActivity activity); - - /** Inject into TouchpadTutorialActivity. */ - @Binds - @IntoMap - @ClassKey(TouchpadTutorialActivity.class) - public abstract Activity bindTouchpadTutorialActivity(TouchpadTutorialActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 9ae63a19473a..32731117a8f6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerIm import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.toast.ToastModule; +import com.android.systemui.touchpad.tutorial.TouchpadKeyboardTutorialModule; import com.android.systemui.unfold.SysUIUnfoldStartableModule; import com.android.systemui.unfold.UnfoldTransitionModule; import com.android.systemui.util.kotlin.SysUICoroutinesModule; @@ -141,6 +142,7 @@ import javax.inject.Named; SysUIUnfoldStartableModule.class, UnfoldTransitionModule.Startables.class, ToastModule.class, + TouchpadKeyboardTutorialModule.class, VolumeModule.class, WallpaperModule.class, ShortcutHelperModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 83fa001d104e..982398579477 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -56,6 +56,7 @@ import com.android.systemui.ambient.touch.TouchMonitor; import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent; import com.android.systemui.ambient.touch.scrim.ScrimManager; import com.android.systemui.communal.domain.interactor.CommunalInteractor; +import com.android.systemui.communal.shared.log.CommunalUiEvent; import com.android.systemui.communal.shared.model.CommunalScenes; import com.android.systemui.complication.Complication; import com.android.systemui.complication.dagger.ComplicationComponent; @@ -407,6 +408,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ @Override public void onWakeRequested() { + mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START); mCommunalInteractor.changeScene(CommunalScenes.Communal, null); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java index 7d11d3214dde..303f91688575 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java @@ -17,6 +17,7 @@ package com.android.systemui.flags; import static com.android.systemui.Flags.exampleFlag; +import static com.android.systemui.Flags.classicFlagsMultiUser; import static com.android.systemui.Flags.sysuiTeamfood; import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS; import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG; @@ -41,6 +42,7 @@ import androidx.annotation.Nullable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.settings.UserContextProvider; import com.android.systemui.util.settings.GlobalSettings; import java.io.PrintWriter; @@ -125,9 +127,14 @@ public class FeatureFlagsClassicDebug implements FeatureFlagsClassic { @Main Resources resources, ServerFlagReader serverFlagReader, @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags, - Restarter restarter) { + Restarter restarter, + UserContextProvider userContextProvider) { mFlagManager = flagManager; - mContext = context; + if (classicFlagsMultiUser()) { + mContext = userContextProvider.createCurrentUserContext(context); + } else { + mContext = context; + } mGlobalSettings = globalSettings; mResources = resources; mSystemProperties = systemProperties; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index d1a84632e0eb..2f41c0b2c1ea 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -62,21 +62,21 @@ import javax.inject.Inject const val TAG = "KeyguardUnlock" /** - * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating - * in during keyguard exit. + * Starting scale factor for the app/launcher surface behind the keyguard, when it's animating in + * during keyguard exit. */ const val SURFACE_BEHIND_START_SCALE_FACTOR = 0.95f /** - * How much to translate the surface behind the keyguard at the beginning of the exit animation, - * in terms of percentage of the surface's height. + * How much to translate the surface behind the keyguard at the beginning of the exit animation, in + * terms of percentage of the surface's height. */ const val SURFACE_BEHIND_START_TRANSLATION_Y = 0.05f /** - * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This - * is expressed as percentage of the surface's height, so 0.66f means the surface will scale up - * from the point at (width / 2, height * 0.66). + * Y coordinate of the pivot point for the scale effect on the surface behind the keyguard. This is + * expressed as percentage of the surface's height, so 0.66f means the surface will scale up from + * the point at (width / 2, height * 0.66). */ const val SURFACE_BEHIND_SCALE_PIVOT_Y = 0.66f @@ -155,19 +155,20 @@ const val UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS = 67L * [notifyStartSurfaceBehindRemoteAnimation] by [KeyguardViewMediator]. */ @SysUISingleton -class KeyguardUnlockAnimationController @Inject constructor( - private val windowManager: WindowManager, - @Main private val resources: Resources, - private val keyguardStateController: KeyguardStateController, - private val - keyguardViewMediator: Lazy<KeyguardViewMediator>, - private val keyguardViewController: KeyguardViewController, - private val featureFlags: FeatureFlags, - private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, - private val statusBarStateController: SysuiStatusBarStateController, - private val notificationShadeWindowController: NotificationShadeWindowController, - private val powerManager: PowerManager, - private val wallpaperManager: WallpaperManager, +open class KeyguardUnlockAnimationController +@Inject +constructor( + private val windowManager: WindowManager, + @Main private val resources: Resources, + private val keyguardStateController: KeyguardStateController, + private val keyguardViewMediator: Lazy<KeyguardViewMediator>, + private val keyguardViewController: KeyguardViewController, + private val featureFlags: FeatureFlags, + private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, + private val statusBarStateController: SysuiStatusBarStateController, + private val notificationShadeWindowController: NotificationShadeWindowController, + private val powerManager: PowerManager, + private val wallpaperManager: WallpaperManager, ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { interface KeyguardUnlockAnimationListener { @@ -221,8 +222,8 @@ class KeyguardUnlockAnimationController @Inject constructor( var playingCannedUnlockAnimation = false /** - * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once - * and should ignore any future changes to the dismiss amount before the animation finishes. + * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once and + * should ignore any future changes to the dismiss amount before the animation finishes. */ var dismissAmountThresholdsReached = false @@ -235,9 +236,7 @@ class KeyguardUnlockAnimationController @Inject constructor( */ private var launcherUnlockController: ILauncherUnlockAnimationController? = null - /** - * Fully qualified class name of the launcher activity - */ + /** Fully qualified class name of the launcher activity */ private var launcherActivityClass: String? = null private val listeners = ArrayList<KeyguardUnlockAnimationListener>() @@ -248,8 +247,8 @@ class KeyguardUnlockAnimationController @Inject constructor( * transition, but that's okay! */ override fun setLauncherUnlockController( - activityClass: String, - callback: ILauncherUnlockAnimationController? + activityClass: String, + callback: ILauncherUnlockAnimationController? ) { launcherActivityClass = activityClass launcherUnlockController = callback @@ -274,8 +273,7 @@ class KeyguardUnlockAnimationController @Inject constructor( * If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned * animation is started in [playCannedUnlockAnimation]. */ - @VisibleForTesting - var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null + @VisibleForTesting var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null private var openingWallpaperTargets: Array<RemoteAnimationTarget>? = null private var closingWallpaperTargets: Array<RemoteAnimationTarget>? = null @@ -291,8 +289,7 @@ class KeyguardUnlockAnimationController @Inject constructor( */ private var surfaceBehindAlpha = 1f - @VisibleForTesting - var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) + @VisibleForTesting var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f) var wallpaperCannedUnlockAnimator = ValueAnimator.ofFloat(0f, 1f) @@ -310,8 +307,7 @@ class KeyguardUnlockAnimationController @Inject constructor( * Animator that animates in the surface behind the keyguard. This is used to play a canned * animation on the surface, if we're not doing a swipe gesture. */ - @VisibleForTesting - val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f) + @VisibleForTesting val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f) /** Rounded corner radius to apply to the surface behind the keyguard. */ private var roundedCornerRadius = 0f @@ -322,8 +318,7 @@ class KeyguardUnlockAnimationController @Inject constructor( * window like any other app. This can be true while [willUnlockWithSmartspaceTransition] is * false, if the smartspace is not available or was not ready in time. */ - @VisibleForTesting - var willUnlockWithInWindowLauncherAnimations: Boolean = false + @VisibleForTesting var willUnlockWithInWindowLauncherAnimations: Boolean = false /** * Whether we called [ILauncherUnlockAnimationController.prepareForUnlock], but have not yet @@ -353,49 +348,64 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceBehindAlpha = valueAnimator.animatedValue as Float updateSurfaceBehindAppearAmount() } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - // If we animated the surface alpha to 0f, it means we cancelled a swipe to - // dismiss. In this case, we should ask the KeyguardViewMediator to end the - // remote animation to hide the surface behind the keyguard, but should *not* - // call onKeyguardExitRemoteAnimationFinished since that will hide the keyguard - // and unlock the device as well as hiding the surface. - if (surfaceBehindAlpha == 0f) { - Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd") - surfaceBehindRemoteAnimationTargets = null - openingWallpaperTargets = null - closingWallpaperTargets = null - keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation( - false /* cancelled */) - } else { - Log.d(TAG, "skip finishSurfaceBehindRemoteAnimation" + - " surfaceBehindAlpha=$surfaceBehindAlpha") + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + // If we animated the surface alpha to 0f, it means we cancelled a swipe to + // dismiss. In this case, we should ask the KeyguardViewMediator to end the + // remote animation to hide the surface behind the keyguard, but should + // *not* call onKeyguardExitRemoteAnimationFinished since that will hide the + // keyguard and unlock the device as well as hiding the surface. + if (surfaceBehindAlpha == 0f) { + Log.d(TAG, "surfaceBehindAlphaAnimator#onAnimationEnd") + surfaceBehindRemoteAnimationTargets = null + openingWallpaperTargets = null + closingWallpaperTargets = null + keyguardViewMediator + .get() + .finishSurfaceBehindRemoteAnimation(false /* cancelled */) + } else { + Log.d( + TAG, + "skip finishSurfaceBehindRemoteAnimation" + + " surfaceBehindAlpha=$surfaceBehindAlpha" + ) + } } } - }) + ) } with(wallpaperCannedUnlockAnimator) { - duration = if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS - else LAUNCHER_ICONS_ANIMATION_DURATION_MS - interpolator = if (fasterUnlockTransition()) Interpolators.LINEAR - else Interpolators.ALPHA_OUT + duration = + if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS + else LAUNCHER_ICONS_ANIMATION_DURATION_MS + interpolator = + if (fasterUnlockTransition()) Interpolators.LINEAR else Interpolators.ALPHA_OUT addUpdateListener { valueAnimator: ValueAnimator -> setWallpaperAppearAmount( - valueAnimator.animatedValue as Float, openingWallpaperTargets) + valueAnimator.animatedValue as Float, + openingWallpaperTargets + ) } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator) { - super.onAnimationStart(animation) - Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0) - } - override fun onAnimationEnd(animation: Animator) { - Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd") - keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) - Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0) + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + super.onAnimationStart(animation) + Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0) + } + + override fun onAnimationEnd(animation: Animator) { + Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd") + keyguardViewMediator + .get() + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation( + false /* cancelled */ + ) + Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0) + } } - }) + ) } if (fasterUnlockTransition()) { @@ -405,7 +415,9 @@ class KeyguardUnlockAnimationController @Inject constructor( interpolator = Interpolators.LINEAR addUpdateListener { valueAnimator: ValueAnimator -> setWallpaperAppearAmount( - valueAnimator.animatedValue as Float, closingWallpaperTargets) + valueAnimator.animatedValue as Float, + closingWallpaperTargets + ) } } } @@ -418,15 +430,19 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceBehindAlpha = valueAnimator.animatedValue as Float setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float) } - addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd") - playingCannedUnlockAnimation = false - keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */ - ) + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + Log.d(TAG, "surfaceBehindEntryAnimator#onAnimationEnd") + playingCannedUnlockAnimation = false + keyguardViewMediator + .get() + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation( + false /* cancelled */ + ) + } } - }) + ) } // Listen for changes in the dismiss amount. @@ -436,9 +452,7 @@ class KeyguardUnlockAnimationController @Inject constructor( resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat() } - /** - * Add a listener to be notified of various stages of the unlock animation. - */ + /** Add a listener to be notified of various stages of the unlock animation. */ fun addKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) { listeners.add(listener) } @@ -454,11 +468,11 @@ class KeyguardUnlockAnimationController @Inject constructor( fun canPerformInWindowLauncherAnimations(): Boolean { // TODO(b/278086361): Refactor in-window animations. return !KeyguardWmStateRefactor.isEnabled && - isSupportedLauncherUnderneath() && - // If the launcher is underneath, but we're about to launch an activity, don't do - // the animations since they won't be visible. - !notificationShadeWindowController.isLaunchingActivity && - launcherUnlockController != null + isSupportedLauncherUnderneath() && + // If the launcher is underneath, but we're about to launch an activity, don't do + // the animations since they won't be visible. + !notificationShadeWindowController.isLaunchingActivity && + launcherUnlockController != null } /** @@ -469,8 +483,11 @@ class KeyguardUnlockAnimationController @Inject constructor( private fun logInWindowAnimationConditions() { Log.wtf(TAG, "canPerformInWindowLauncherAnimations expected all of these to be true: ") Log.wtf(TAG, " isNexusLauncherUnderneath: ${isSupportedLauncherUnderneath()}") - Log.wtf(TAG, " !notificationShadeWindowController.isLaunchingActivity: " + - "${!notificationShadeWindowController.isLaunchingActivity}") + Log.wtf( + TAG, + " !notificationShadeWindowController.isLaunchingActivity: " + + "${!notificationShadeWindowController.isLaunchingActivity}" + ) Log.wtf(TAG, " launcherUnlockController != null: ${launcherUnlockController != null}") Log.wtf(TAG, " !isFoldable(context): ${!isFoldable(resources)}") } @@ -480,8 +497,10 @@ class KeyguardUnlockAnimationController @Inject constructor( * changed. */ override fun onKeyguardGoingAwayChanged() { - if (keyguardStateController.isKeyguardGoingAway && - !statusBarStateController.leaveOpenOnKeyguardHide()) { + if ( + keyguardStateController.isKeyguardGoingAway && + !statusBarStateController.leaveOpenOnKeyguardHide() + ) { prepareForInWindowLauncherAnimations() } @@ -489,16 +508,22 @@ class KeyguardUnlockAnimationController @Inject constructor( // make sure that we've left the launcher at 100% unlocked. This is a fail-safe to prevent // against "tiny launcher" and similar states where the launcher is left in the prepared to // animate state. - if (!keyguardStateController.isKeyguardGoingAway && - willUnlockWithInWindowLauncherAnimations) { + if ( + !keyguardStateController.isKeyguardGoingAway && willUnlockWithInWindowLauncherAnimations + ) { try { - launcherUnlockController?.setUnlockAmount(1f, - biometricUnlockControllerLazy.get().isWakeAndUnlock /* forceIfAnimating */) + launcherUnlockController?.setUnlockAmount( + 1f, + biometricUnlockControllerLazy.get().isWakeAndUnlock /* forceIfAnimating */ + ) } catch (e: DeadObjectException) { - Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null in " + + Log.e( + TAG, + "launcherUnlockAnimationController was dead, but non-null in " + "onKeyguardGoingAwayChanged(). Catching exception as this should mean " + "Launcher is in the process of being destroyed, but the IPC to System UI " + - "telling us hasn't arrived yet.") + "telling us hasn't arrived yet." + ) } } } @@ -525,22 +550,26 @@ class KeyguardUnlockAnimationController @Inject constructor( // Grab the bounds of our lockscreen smartspace and send them to launcher so they can // position their smartspace there initially, then animate it to its resting position. if (willUnlockWithSmartspaceTransition) { - lockscreenSmartspaceBounds = Rect().apply { - lockscreenSmartspace!!.getBoundsOnScreen(this) - - // The smartspace container on the lockscreen has left and top padding to align it - // with other lockscreen content. This padding is inside the bounds on screen, so - // add it to those bounds so that the padding-less launcher smartspace is properly - // aligned. - offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop) - - // Also offset by the current card's top padding, if it has any. This allows us to - // align the tops of the lockscreen/launcher smartspace cards. Some cards, such as - // the three-line date/weather/alarm card, only have three lines on lockscreen but - // two on launcher. - offset(0, (lockscreenSmartspace - as? BcSmartspaceDataPlugin.SmartspaceView)?.currentCardTopPadding ?: 0) - } + lockscreenSmartspaceBounds = + Rect().apply { + lockscreenSmartspace!!.getBoundsOnScreen(this) + + // The smartspace container on the lockscreen has left and top padding to align + // it with other lockscreen content. This padding is inside the bounds on + // screen, so add it to those bounds so that the padding-less launcher + // smartspace is properly aligned. + offset(lockscreenSmartspace!!.paddingLeft, lockscreenSmartspace!!.paddingTop) + + // Also offset by the current card's top padding, if it has any. This allows us + // to align the tops of the lockscreen/launcher smartspace cards. Some cards, + // such as the three-line date/weather/alarm card, only have three lines on + // lockscreen but two on launcher. + offset( + 0, + (lockscreenSmartspace as? BcSmartspaceDataPlugin.SmartspaceView) + ?.currentCardTopPadding ?: 0 + ) + } } // Currently selected lockscreen smartspace page, or -1 if it's not available. @@ -583,8 +612,8 @@ class KeyguardUnlockAnimationController @Inject constructor( requestedShowSurfaceBehindKeyguard: Boolean ) { if (surfaceTransactionApplier == null) { - surfaceTransactionApplier = SyncRtSurfaceTransactionApplier( - keyguardViewController.viewRootImpl.view) + surfaceTransactionApplier = + SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view) } surfaceBehindRemoteAnimationTargets = targets @@ -603,8 +632,10 @@ class KeyguardUnlockAnimationController @Inject constructor( // surface behind the keyguard to finish unlocking. if (keyguardStateController.isFlingingToDismissKeyguard) { playCannedUnlockAnimation() - } else if (keyguardStateController.isDismissingFromSwipe && - willUnlockWithInWindowLauncherAnimations) { + } else if ( + keyguardStateController.isDismissingFromSwipe && + willUnlockWithInWindowLauncherAnimations + ) { // If we're swiping to unlock to the Launcher, and can play in-window animations, // make the launcher surface fully visible and play the in-window unlock animation // on the launcher icons. System UI will remain locked, using the swipe-to-unlock @@ -615,19 +646,23 @@ class KeyguardUnlockAnimationController @Inject constructor( try { launcherUnlockController?.playUnlockAnimation( - true, - unlockAnimationDurationMs() + cannedUnlockStartDelayMs(), - 0 /* startDelay */) + true, + unlockAnimationDurationMs() + cannedUnlockStartDelayMs(), + 0 /* startDelay */ + ) } catch (e: DeadObjectException) { // Hello! If you are here investigating a bug where Launcher is blank (no icons) // then the below assumption about Launcher's destruction was incorrect. This // would mean prepareToUnlock was called (blanking Launcher in preparation for // the beginning of the unlock animation), but then somehow we were unable to // call playUnlockAnimation to animate the icons back in. - Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null. " + + Log.e( + TAG, + "launcherUnlockAnimationController was dead, but non-null. " + "Catching exception as this should mean Launcher is in the process " + "of being destroyed, but the IPC to System UI telling us hasn't " + - "arrived yet.") + "arrived yet." + ) } launcherPreparedForUnlock = false @@ -643,15 +678,18 @@ class KeyguardUnlockAnimationController @Inject constructor( } // Notify if waking from AOD only - val isWakeAndUnlockNotFromDream = biometricUnlockControllerLazy.get().isWakeAndUnlock && - biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM + val isWakeAndUnlockNotFromDream = + biometricUnlockControllerLazy.get().isWakeAndUnlock && + biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM listeners.forEach { it.onUnlockAnimationStarted( playingCannedUnlockAnimation /* playingCannedAnimation */, isWakeAndUnlockNotFromDream /* isWakeAndUnlockNotFromDream */, cannedUnlockStartDelayMs() /* unlockStartDelay */, - LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */) } + LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */ + ) + } // Finish the keyguard remote animation if the dismiss amount has crossed the threshold. // Check it here in case there is no more change to the dismiss amount after the last change @@ -685,8 +723,9 @@ class KeyguardUnlockAnimationController @Inject constructor( biometricUnlockControllerLazy.get().isWakeAndUnlock -> { Log.d(TAG, "playCannedUnlockAnimation, isWakeAndUnlock") setSurfaceBehindAppearAmount(1f) - keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) + keyguardViewMediator + .get() + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) } // Otherwise, we're doing a normal full-window unlock. Start this animator, which will @@ -698,8 +737,11 @@ class KeyguardUnlockAnimationController @Inject constructor( } if (launcherPreparedForUnlock && !willUnlockWithInWindowLauncherAnimations) { - Log.wtf(TAG, "Launcher is prepared for unlock, so we should have started the " + - "in-window animation, however we apparently did not.") + Log.wtf( + TAG, + "Launcher is prepared for unlock, so we should have started the " + + "in-window animation, however we apparently did not." + ) logInWindowAnimationConditions() } } @@ -708,7 +750,6 @@ class KeyguardUnlockAnimationController @Inject constructor( * Unlock to the launcher, using in-window animations, and the smartspace shared element * transition if possible. */ - @VisibleForTesting fun unlockToLauncherWithInWindowAnimations() { surfaceBehindAlpha = 1f @@ -717,26 +758,32 @@ class KeyguardUnlockAnimationController @Inject constructor( try { // Begin the animation, waiting for the shade to animate out. launcherUnlockController?.playUnlockAnimation( - true /* unlocked */, - LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */, - cannedUnlockStartDelayMs() /* startDelay */) + true /* unlocked */, + LAUNCHER_ICONS_ANIMATION_DURATION_MS /* duration */, + cannedUnlockStartDelayMs() /* startDelay */ + ) } catch (e: DeadObjectException) { // Hello! If you are here investigating a bug where Launcher is blank (no icons) // then the below assumption about Launcher's destruction was incorrect. This // would mean prepareToUnlock was called (blanking Launcher in preparation for // the beginning of the unlock animation), but then somehow we were unable to // call playUnlockAnimation to animate the icons back in. - Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null. " + + Log.e( + TAG, + "launcherUnlockAnimationController was dead, but non-null. " + "Catching exception as this should mean Launcher is in the process " + "of being destroyed, but the IPC to System UI telling us hasn't " + - "arrived yet.") + "arrived yet." + ) } launcherPreparedForUnlock = false // Now that the Launcher surface (with its smartspace positioned identically to ours) is // visible, hide our smartspace. - if (lockscreenSmartspace?.visibility == View.VISIBLE) { + if ( + shouldPerformSmartspaceTransition() && lockscreenSmartspace?.visibility == View.VISIBLE + ) { lockscreenSmartspace?.visibility = View.INVISIBLE } @@ -747,22 +794,31 @@ class KeyguardUnlockAnimationController @Inject constructor( fadeOutWallpaper() } - handler.postDelayed({ - if (keyguardViewMediator.get().isShowingAndNotOccluded && - !keyguardStateController.isKeyguardGoingAway) { - Log.e(TAG, "Finish keyguard exit animation delayed Runnable ran, but we are " + - "showing and not going away.") - return@postDelayed - } + handler.postDelayed( + { + if ( + keyguardViewMediator.get().isShowingAndNotOccluded && + !keyguardStateController.isKeyguardGoingAway + ) { + Log.e( + TAG, + "Finish keyguard exit animation delayed Runnable ran, but we are " + + "showing and not going away." + ) + return@postDelayed + } - if (openingWallpaperTargets?.isNotEmpty() == true) { - fadeInWallpaper() - hideKeyguardViewAfterRemoteAnimation() - } else { - keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) - } - }, cannedUnlockStartDelayMs()) + if (openingWallpaperTargets?.isNotEmpty() == true) { + fadeInWallpaper() + hideKeyguardViewAfterRemoteAnimation() + } else { + keyguardViewMediator + .get() + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) + } + }, + cannedUnlockStartDelayMs() + ) } /** @@ -784,12 +840,14 @@ class KeyguardUnlockAnimationController @Inject constructor( // interaction tight. if (keyguardStateController.isFlingingToDismissKeyguard) { setSurfaceBehindAppearAmount(keyguardStateController.dismissAmount) - } else if (keyguardStateController.isDismissingFromSwipe || - keyguardStateController.isSnappingKeyguardBackAfterSwipe) { + } else if ( + keyguardStateController.isDismissingFromSwipe || + keyguardStateController.isSnappingKeyguardBackAfterSwipe + ) { val totalSwipeDistanceToDismiss = - (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD) + (DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD) val swipedDistanceSoFar: Float = - keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD + keyguardStateController.dismissAmount - DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD val progress = swipedDistanceSoFar / totalSwipeDistanceToDismiss setSurfaceBehindAppearAmount(progress) } @@ -801,10 +859,13 @@ class KeyguardUnlockAnimationController @Inject constructor( // If the surface is visible or it's about to be, start updating its appearance to // reflect the new dismiss amount. - if ((keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || - keyguardViewMediator.get() + if ( + (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || + keyguardViewMediator + .get() .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) && - !playingCannedUnlockAnimation) { + !playingCannedUnlockAnimation + ) { updateSurfaceBehindAppearAmount() } } @@ -838,11 +899,15 @@ class KeyguardUnlockAnimationController @Inject constructor( val dismissAmount = keyguardStateController.dismissAmount - if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && - !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { + if ( + dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && + !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() + ) { keyguardViewMediator.get().showSurfaceBehindKeyguard() - } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && - keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { + } else if ( + dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && + keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() + ) { // We're no longer past the threshold but we are showing the surface. Animate it // out. keyguardViewMediator.get().hideSurfaceBehindKeyguard() @@ -868,22 +933,27 @@ class KeyguardUnlockAnimationController @Inject constructor( } // no-op if animation is not requested yet. - if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || - !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { + if ( + !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || + !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe + ) { return } val dismissAmount = keyguardStateController.dismissAmount - if (dismissAmount >= 1f || + if ( + dismissAmount >= 1f || (keyguardStateController.isDismissingFromSwipe && - // Don't hide if we're flinging during a swipe, since we need to finish - // animating it out. This will be called again after the fling ends. - !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && - dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) { + // Don't hide if we're flinging during a swipe, since we need to finish + // animating it out. This will be called again after the fling ends. + !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && + dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD) + ) { setSurfaceBehindAppearAmount(1f) dismissAmountThresholdsReached = true - keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) + keyguardViewMediator + .get() + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) } } @@ -894,51 +964,56 @@ class KeyguardUnlockAnimationController @Inject constructor( * wallpapers, this transitions between the two wallpapers */ fun setSurfaceBehindAppearAmount(amount: Float, wallpapers: Boolean = true) { - val animationAlpha = when { - // If we're snapping the keyguard back, immediately begin fading it out. - keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount - // If the screen has turned back off, the unlock animation is going to be cancelled, - // so set the surface alpha to 0f so it's no longer visible. - !powerManager.isInteractive -> 0f - else -> surfaceBehindAlpha - } + val animationAlpha = + when { + // If we're snapping the keyguard back, immediately begin fading it out. + keyguardStateController.isSnappingKeyguardBackAfterSwipe -> amount + // If the screen has turned back off, the unlock animation is going to be cancelled, + // so set the surface alpha to 0f so it's no longer visible. + !powerManager.isInteractive -> 0f + else -> surfaceBehindAlpha + } surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget -> if (!KeyguardWmStateRefactor.isEnabled) { val surfaceHeight: Int = - surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height() + surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height() - var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR + - (1f - SURFACE_BEHIND_START_SCALE_FACTOR) * - MathUtils.clamp(amount, 0f, 1f)) + var scaleFactor = + (SURFACE_BEHIND_START_SCALE_FACTOR + + (1f - SURFACE_BEHIND_START_SCALE_FACTOR) * MathUtils.clamp(amount, 0f, 1f)) // If we're dismissing via swipe to the Launcher, we'll play in-window scale // animations, so don't also scale the window. - if (keyguardStateController.isDismissingFromSwipe && - willUnlockWithInWindowLauncherAnimations) { + if ( + keyguardStateController.isDismissingFromSwipe && + willUnlockWithInWindowLauncherAnimations + ) { scaleFactor = 1f } // Translate up from the bottom. surfaceBehindMatrix.setTranslate( - surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(), - surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() + - surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount) + surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(), + surfaceBehindRemoteAnimationTarget.screenSpaceBounds.top.toFloat() + + surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount) ) // Scale up from a point at the center-bottom of the surface. surfaceBehindMatrix.postScale( - scaleFactor, - scaleFactor, - keyguardViewController.viewRootImpl.width / 2f, - surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y + scaleFactor, + scaleFactor, + keyguardViewController.viewRootImpl.width / 2f, + surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y ) // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is // unable to draw val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash - if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && - sc?.isValid == true) { + if ( + keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && + sc?.isValid == true + ) { with(SurfaceControl.Transaction()) { setMatrix(sc, surfaceBehindMatrix, tmpFloat) setCornerRadius(sc, roundedCornerRadius) @@ -947,12 +1022,13 @@ class KeyguardUnlockAnimationController @Inject constructor( } } else { applyParamsToSurface( - SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( - surfaceBehindRemoteAnimationTarget.leash) - .withMatrix(surfaceBehindMatrix) - .withCornerRadius(roundedCornerRadius) - .withAlpha(animationAlpha) - .build() + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( + surfaceBehindRemoteAnimationTarget.leash + ) + .withMatrix(surfaceBehindMatrix) + .withCornerRadius(roundedCornerRadius) + .withAlpha(animationAlpha) + .build() ) } } @@ -969,8 +1045,8 @@ class KeyguardUnlockAnimationController @Inject constructor( val fadeOutStart = LOCK_WALLPAPER_FADE_OUT_START_DELAY / total val fadeOutEnd = fadeOutStart + LOCK_WALLPAPER_FADE_OUT_DURATION / total - val fadeOutAmount = ((amount - fadeOutStart) / (fadeOutEnd - fadeOutStart)) - .coerceIn(0f, 1f) + val fadeOutAmount = + ((amount - fadeOutStart) / (fadeOutEnd - fadeOutStart)).coerceIn(0f, 1f) setWallpaperAppearAmount(fadeInAmount, openingWallpaperTargets) setWallpaperAppearAmount(1 - fadeOutAmount, closingWallpaperTargets) @@ -984,18 +1060,19 @@ class KeyguardUnlockAnimationController @Inject constructor( // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is // unable to draw val sc: SurfaceControl? = wallpaper.leash - if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && - sc?.isValid == true) { + if ( + keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE && + sc?.isValid == true + ) { with(SurfaceControl.Transaction()) { setAlpha(sc, animationAlpha) apply() } } else { applyParamsToSurface( - SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( - wallpaper.leash) - .withAlpha(animationAlpha) - .build() + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(wallpaper.leash) + .withAlpha(animationAlpha) + .build() ) } } @@ -1019,9 +1096,9 @@ class KeyguardUnlockAnimationController @Inject constructor( } if (!showKeyguard) { - // Make sure we made the surface behind fully visible, just in case. It should already be - // fully visible. The exit animation is finished, and we should not hold the leash anymore, - // so forcing it to 1f. + // Make sure we made the surface behind fully visible, just in case. It should already + // be fully visible. The exit animation is finished, and we should not hold the leash + // anymore, so forcing it to 1f. surfaceBehindAlpha = 1f setSurfaceBehindAppearAmount(1f) @@ -1061,13 +1138,16 @@ class KeyguardUnlockAnimationController @Inject constructor( if (!KeyguardWmStateRefactor.isEnabled) { keyguardViewController.hide( - surfaceBehindRemoteAnimationStartTime, - 0 /* fadeOutDuration */ + surfaceBehindRemoteAnimationStartTime, + 0 /* fadeOutDuration */ ) } } else { - Log.i(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + - "showing. Ignoring...") + Log.i( + TAG, + "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + + "showing. Ignoring..." + ) } } @@ -1099,7 +1179,8 @@ class KeyguardUnlockAnimationController @Inject constructor( surfaceBehindAlphaAnimator.reverse() } - private fun shouldPerformSmartspaceTransition(): Boolean { + /** Note: declared open for ease of testing */ + open fun shouldPerformSmartspaceTransition(): Boolean { // Feature is disabled, so we don't want to. if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) { return false @@ -1107,9 +1188,11 @@ class KeyguardUnlockAnimationController @Inject constructor( // If our controllers are null, or we haven't received a smartspace state from Launcher yet, // we will not be doing any smartspace transitions today. - if (launcherUnlockController == null || - lockscreenSmartspace == null || - launcherSmartspaceState == null) { + if ( + launcherUnlockController == null || + lockscreenSmartspace == null || + launcherSmartspaceState == null + ) { return false } @@ -1135,8 +1218,10 @@ class KeyguardUnlockAnimationController @Inject constructor( // element transition is if we're doing a biometric unlock. Otherwise, it means the bouncer // is showing, and you can't see the lockscreen smartspace, so a shared element transition // would not make sense. - if (!keyguardStateController.canDismissLockScreen() && - !biometricUnlockControllerLazy.get().isBiometricUnlock) { + if ( + !keyguardStateController.canDismissLockScreen() && + !biometricUnlockControllerLazy.get().isBiometricUnlock + ) { return false } @@ -1175,9 +1260,7 @@ class KeyguardUnlockAnimationController @Inject constructor( return willUnlockWithSmartspaceTransition } - /** - * Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'. - */ + /** Whether the RemoteAnimation on the app/launcher surface behind the keyguard is 'running'. */ fun isAnimatingBetweenKeyguardAndSurfaceBehind(): Boolean { return keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehind } @@ -1196,39 +1279,38 @@ class KeyguardUnlockAnimationController @Inject constructor( * in-window/shared element transitions! */ fun isSupportedLauncherUnderneath(): Boolean { - return launcherActivityClass?.let { ActivityManagerWrapper.getInstance() - .runningTask?.topActivity?.className?.equals(it) } - ?: false + return launcherActivityClass?.let { + ActivityManagerWrapper.getInstance().runningTask?.topActivity?.className?.equals(it) + } ?: false } /** - * Temporary method for b/298186160 - * TODO (b/298186160) replace references with the constant itself when flag is removed + * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant + * itself when flag is removed */ private fun cannedUnlockStartDelayMs(): Long { return if (fasterUnlockTransition()) CANNED_UNLOCK_START_DELAY - else LEGACY_CANNED_UNLOCK_START_DELAY + else LEGACY_CANNED_UNLOCK_START_DELAY } /** - * Temporary method for b/298186160 - * TODO (b/298186160) replace references with the constant itself when flag is removed + * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant + * itself when flag is removed */ private fun unlockAnimationDurationMs(): Long { return if (fasterUnlockTransition()) UNLOCK_ANIMATION_DURATION_MS - else LEGACY_UNLOCK_ANIMATION_DURATION_MS + else LEGACY_UNLOCK_ANIMATION_DURATION_MS } /** - * Temporary method for b/298186160 - * TODO (b/298186160) replace references with the constant itself when flag is removed + * Temporary method for b/298186160 TODO (b/298186160) replace references with the constant + * itself when flag is removed */ private fun surfaceBehindFadeOutStartDelayMs(): Long { return if (fasterUnlockTransition()) UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS - else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS + else LEGACY_UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS } - companion object { fun isFoldable(resources: Resources): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e46a7cbf9bb0..0b3d0f7160f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1278,6 +1278,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, initAlphaForAnimationTargets(wallpapers); if (isDream) { mDreamViewModel.get().startTransitionFromDream(); + } else { + mCommunalTransitionViewModel.get().snapToCommunal(); } mUnoccludeFinishedCallback = finishedCallback; return; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index b44a8cf70328..a915241fc646 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -26,7 +26,6 @@ import com.android.systemui.keyguard.KeyguardWmStateRefactor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes @@ -91,12 +90,12 @@ constructor( edgeWithoutSceneContainer = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) ) - .map<TransitionStep, Boolean?> { + .map { // The alt bouncer is pretty fast to hide, so start the surface behind animation // around 30%. it.value > 0.3f } - .onStart { + .onStart<Boolean?> { // Default to null ("don't care, use a reasonable default"). emit(null) } @@ -145,6 +144,7 @@ constructor( } } else { if (isIdleOnCommunal) { + if (SceneContainerFlag.isEnabled) return@collect KeyguardState.GLANCEABLE_HUB } else if (isOccluded) { KeyguardState.OCCLUDED @@ -158,7 +158,6 @@ constructor( } private fun listenForAlternateBouncerToGone() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (KeyguardWmStateRefactor.isEnabled) { // Handled via #dismissAlternateBouncer. @@ -170,9 +169,9 @@ constructor( keyguardInteractor.isKeyguardGoingAway.filter { it }.map {}, // map to Unit keyguardInteractor.isKeyguardOccluded.flatMapLatest { keyguardOccluded -> if (keyguardOccluded) { - primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled.drop( - 1 - ) // drop the initial state + primaryBouncerInteractor.keyguardAuthenticatedBiometricsHandled + // drop the initial state + .drop(1) } else { emptyFlow() } @@ -184,7 +183,6 @@ constructor( } private fun listenForAlternateBouncerToPrimaryBouncer() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.primaryBouncerShowing diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index 117dbcfe52c8..f3bd0e9496b1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -61,6 +61,7 @@ constructor( ) { override fun start() { + if (SceneContainerFlag.isEnabled) return listenForDreamingLockscreenHostedToLockscreen() listenForDreamingLockscreenHostedToGone() listenForDreamingLockscreenHostedToDozing() @@ -96,8 +97,6 @@ constructor( } private fun listenForDreamingLockscreenHostedToPrimaryBouncer() { - // TODO(b/336576536): Check if adaptation for scene framework is needed - if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.primaryBouncerShowing .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing } @@ -106,8 +105,6 @@ constructor( } private fun listenForDreamingLockscreenHostedToGone() { - // TODO(b/336576536): Check if adaptation for scene framework is needed - if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.biometricUnlockState .filterRelevantKeyguardStateAnd { biometricUnlockState -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 4c3a75e765b6..3775d191949e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -100,7 +100,6 @@ constructor( private fun listenForDreamingToGlanceableHub() { if (!communalSettingsInteractor.isCommunalFlagEnabled()) return - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) { glanceableHubTransitions.listenForGlanceableHubTransition( @@ -195,7 +194,7 @@ constructor( private fun listenForDreamingToGoneWhenDismissable() { if (SceneContainerFlag.isEnabled) { - return // TODO(b/336576536): Check if adaptation for scene framework is needed + return } if (KeyguardWmStateRefactor.isEnabled) { @@ -217,7 +216,7 @@ constructor( } private fun listenForDreamingToGoneFromBiometricUnlock() { - // TODO(b/336576536): Check if adaptation for scene framework is needed + // TODO(b/353542570): Adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.biometricUnlockState diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 6b1be93c988a..91ee2872fd22 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -75,7 +75,6 @@ constructor( ) { override fun start() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (!communalSettingsInteractor.isCommunalFlagEnabled()) { return diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index ef76f3837889..8f4110c7cc57 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -69,7 +69,7 @@ constructor( ) { override fun start() { - // TODO(b/336576536): Check if adaptation for scene framework is needed + // KeyguardState.GONE does not exist with SceneContainerFlag enabled if (SceneContainerFlag.isEnabled) return listenForGoneToAodOrDozing() listenForGoneToDreaming() @@ -100,21 +100,19 @@ constructor( } } - if (!SceneContainerFlag.isEnabled) { - scope.launch { - keyguardRepository.isKeyguardEnabled - .filterRelevantKeyguardStateAnd { enabled -> enabled } - .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled) - .filter { reshow -> reshow } - .collect { - startTransitionTo( - KeyguardState.LOCKSCREEN, - ownerReason = - "Keyguard was re-enabled, and we weren't GONE when it " + - "was originally disabled" - ) - } - } + scope.launch { + keyguardRepository.isKeyguardEnabled + .filterRelevantKeyguardStateAnd { enabled -> enabled } + .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled) + .filter { reshow -> reshow } + .collect { + startTransitionTo( + KeyguardState.LOCKSCREEN, + ownerReason = + "Keyguard was re-enabled, and we weren't GONE when it " + + "was originally disabled" + ) + } } } else { scope.launch("$TAG#listenForGoneToLockscreenOrHub") { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 16c014f451f3..206bbc51f793 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -351,7 +351,6 @@ constructor( * keyguard transition. */ private fun listenForLockscreenToGlanceableHub() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (!communalSettingsInteractor.isCommunalFlagEnabled()) { return diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 2f320409f231..710b710aa7d5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -143,7 +143,6 @@ constructor( if (restartDreamOnUnocclude() && dreamFromOccluded) { startTransitionTo(KeyguardState.DREAMING) } else if (isIdleOnCommunal || showCommunalFromOccluded) { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (communalSceneKtfRefactor()) { communalSceneInteractor.changeScene( @@ -159,8 +158,6 @@ constructor( } private fun listenForOccludedToGone() { - // TODO(b/336576536): Check if adaptation for scene framework is needed - if (SceneContainerFlag.isEnabled) return if (KeyguardWmStateRefactor.isEnabled) { // We don't think OCCLUDED to GONE is possible. You should always have to go via a // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard @@ -168,7 +165,8 @@ constructor( // If we're wrong - sorry, add it back here. return } - + // TODO(b/353545202): Adaptation for scene framework is needed + if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.isKeyguardOccluded .sample(keyguardInteractor.isKeyguardShowing, ::Pair) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 9adcaa229ae2..e2d7851daf7e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -103,7 +103,6 @@ constructor( } private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (KeyguardWmStateRefactor.isEnabled) { scope.launch { @@ -177,13 +176,11 @@ constructor( } private fun listenForPrimaryBouncerToAsleep() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return scope.launch { listenForSleepTransition() } } private fun listenForPrimaryBouncerToDreamingLockscreenHosted() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return scope.launch { keyguardInteractor.primaryBouncerShowing @@ -197,7 +194,6 @@ constructor( } private fun listenForPrimaryBouncerToGone() { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return if (KeyguardWmStateRefactor.isEnabled) { // This is handled in KeyguardSecurityContainerController and diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt index af1ce2bfcdde..f9ab1bbcc741 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt @@ -50,7 +50,6 @@ constructor( fromState: KeyguardState, toState: KeyguardState, ) { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return val toScene = if (fromState == KeyguardState.GLANCEABLE_HUB) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt index 1152d7040a38..1c445a703bc4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt @@ -27,6 +27,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCE import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolver +import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolver import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter @@ -57,6 +59,8 @@ constructor( @Application private val applicationScope: CoroutineScope, sceneInteractor: SceneInteractor, deviceEntryInteractor: DeviceEntryInteractor, + quickSettingsSceneFamilyResolver: QuickSettingsSceneFamilyResolver, + notifShadeSceneFamilyResolver: NotifShadeSceneFamilyResolver, ) { val dismissAction: Flow<DismissAction> = repository.dismissAction @@ -96,10 +100,8 @@ constructor( deviceEntryInteractor.isUnlocked, ) { scene, isUnlocked -> isUnlocked && - (scene == Scenes.Shade || - scene == Scenes.NotificationsShade || - scene == Scenes.QuickSettings || - scene == Scenes.QuickSettingsShade) + (quickSettingsSceneFamilyResolver.includesScene(scene) || + notifShadeSceneFamilyResolver.includesScene(scene)) } .distinctUntilChanged() val executeDismissAction: Flow<() -> KeyguardDone> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index f9bfaff80090..797d4667c56d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -398,7 +398,6 @@ constructor( * including KeyguardSecurityContainerController and WindowManager. */ fun startDismissKeyguardTransition(reason: String = "") { - // TODO(b/336576536): Check if adaptation for scene framework is needed if (SceneContainerFlag.isEnabled) return Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") when (val startedState = repository.currentTransitionInfoInternal.value.to) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt index 1c6323594c70..3e6d5da38257 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt @@ -247,6 +247,7 @@ constructor( lp.height = ViewGroup.LayoutParams.MATCH_PARENT lp.width = ViewGroup.LayoutParams.MATCH_PARENT bgView.layoutParams = lp + bgView.alpha = 0f } fun getIconState(icon: IconType, aod: Boolean): IntArray { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt index 630dcca56dd9..15892e9e1c85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt @@ -25,7 +25,6 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes @@ -35,93 +34,70 @@ import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.util.kotlin.filterValuesNotNull import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.stateIn /** Models UI state and handles user input for the lockscreen scene. */ @SysUISingleton class LockscreenSceneViewModel @Inject constructor( - @Application applicationScope: CoroutineScope, - deviceEntryInteractor: DeviceEntryInteractor, - communalInteractor: CommunalInteractor, - shadeInteractor: ShadeInteractor, + private val deviceEntryInteractor: DeviceEntryInteractor, + private val communalInteractor: CommunalInteractor, + private val shadeInteractor: ShadeInteractor, val touchHandling: KeyguardTouchHandlingViewModel, val notifications: NotificationsPlaceholderViewModel, ) { - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - shadeInteractor.isShadeTouchable - .flatMapLatest { isShadeTouchable -> - if (!isShadeTouchable) { - flowOf(emptyMap()) - } else { - combine( - deviceEntryInteractor.isUnlocked, - communalInteractor.isCommunalAvailable, - shadeInteractor.shadeMode, - ) { isDeviceUnlocked, isCommunalAvailable, shadeMode -> - destinationScenes( - isDeviceUnlocked = isDeviceUnlocked, - isCommunalAvailable = isCommunalAvailable, - shadeMode = shadeMode, + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + shadeInteractor.isShadeTouchable.flatMapLatest { isShadeTouchable -> + if (!isShadeTouchable) { + flowOf(emptyMap()) + } else { + combine( + deviceEntryInteractor.isUnlocked, + communalInteractor.isCommunalAvailable, + shadeInteractor.shadeMode, + ) { isDeviceUnlocked, isCommunalAvailable, shadeMode -> + val notifShadeSceneKey = + UserActionResult( + toScene = SceneFamilies.NotifShade, + transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }, ) - } - } - } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = - destinationScenes( - isDeviceUnlocked = deviceEntryInteractor.isUnlocked.value, - isCommunalAvailable = false, - shadeMode = shadeInteractor.shadeMode.value, - ), - ) - private fun destinationScenes( - isDeviceUnlocked: Boolean, - isCommunalAvailable: Boolean, - shadeMode: ShadeMode, - ): Map<UserAction, UserActionResult> { - val notifShadeSceneKey = - UserActionResult( - toScene = SceneFamilies.NotifShade, - transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }, - ) + mapOf( + Swipe.Left to + UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable }, + Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer, - return mapOf( - Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable }, - Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer, + // Swiping down from the top edge goes to QS (or shade if in split shade + // mode). + swipeDownFromTop(pointerCount = 1) to + if (shadeMode is ShadeMode.Single) { + UserActionResult(Scenes.QuickSettings) + } else { + notifShadeSceneKey + }, - // Swiping down from the top edge goes to QS (or shade if in split shade mode). - swipeDownFromTop(pointerCount = 1) to - if (shadeMode is ShadeMode.Single) { - UserActionResult(Scenes.QuickSettings) - } else { - notifShadeSceneKey - }, + // TODO(b/338577208): Remove once we add Dual Shade invocation zones. + swipeDownFromTop(pointerCount = 2) to + UserActionResult( + toScene = SceneFamilies.QuickSettings, + transitionKey = + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ), - // TODO(b/338577208): Remove once we add Dual Shade invocation zones. - swipeDownFromTop(pointerCount = 2) to - UserActionResult( - toScene = SceneFamilies.QuickSettings, - transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - ), - - // Swiping down, not from the edge, always navigates to the notif shade scene. - swipeDown(pointerCount = 1) to notifShadeSceneKey, - swipeDown(pointerCount = 2) to notifShadeSceneKey, - ) - .filterValuesNotNull() - } + // Swiping down, not from the edge, always navigates to the notif shade + // scene. + swipeDown(pointerCount = 1) to notifShadeSceneKey, + swipeDown(pointerCount = 2) to notifShadeSceneKey, + ) + .filterValuesNotNull() + } + } + } private fun swipeDownFromTop(pointerCount: Int): Swipe { return Swipe( diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt index 1f7471668355..fa8e13ab2b72 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt @@ -25,9 +25,8 @@ import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeAlignment import javax.inject.Inject -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf /** Models UI state and handles user input for the Notifications Shade scene. */ @SysUISingleton @@ -36,16 +35,15 @@ class NotificationsShadeSceneViewModel constructor( shadeInteractor: ShadeInteractor, ) { - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - MutableStateFlow( - mapOf( - if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) { - Swipe.Up - } else { - Swipe.Down - } to SceneFamilies.Home, - Back to SceneFamilies.Home, - ) + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + flowOf( + mapOf( + if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) { + Swipe.Up + } else { + Swipe.Down + } to SceneFamilies.Home, + Back to SceneFamilies.Home, ) - .asStateFlow() + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java index edc49cac2f92..f0183360bea9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java @@ -489,6 +489,10 @@ public class InternetDialogDelegate implements mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE); mMobileToggleDivider.setVisibility( mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE); + int primaryColor = isNetworkConnected + ? R.color.connected_network_primary_color + : R.color.disconnected_network_primary_color; + mMobileToggleDivider.setBackgroundColor(dialog.getContext().getColor(primaryColor)); // Display the info for the non-DDS if it's actively being used int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt index 79cdfec182ae..b1cc55d03b04 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt @@ -25,7 +25,6 @@ import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel @@ -38,20 +37,17 @@ import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn /** Models UI state and handles user input for the quick settings scene. */ @SysUISingleton class QuickSettingsSceneViewModel @Inject constructor( - @Application private val applicationScope: CoroutineScope, val brightnessMirrorViewModel: BrightnessMirrorViewModel, val shadeHeaderViewModel: ShadeHeaderViewModel, val qsSceneAdapter: QSSceneAdapter, @@ -61,55 +57,35 @@ constructor( sceneBackInteractor: SceneBackInteractor, val mediaCarouselInteractor: MediaCarouselInteractor, ) { - private val backScene: StateFlow<SceneKey> = + private val backScene: Flow<SceneKey> = sceneBackInteractor.backScene .filter { it != Scenes.QuickSettings } .map { it ?: Scenes.Shade } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = Scenes.Shade, - ) - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = combine( - qsSceneAdapter.isCustomizerShowing, - backScene, - transform = ::destinationScenes, - ) - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = - destinationScenes( - isCustomizing = qsSceneAdapter.isCustomizerShowing.value, - backScene = backScene.value, - ), - ) + qsSceneAdapter.isCustomizerShowing, + backScene, + ) { isCustomizing, backScene -> + buildMap<UserAction, UserActionResult> { + if (isCustomizing) { + // TODO(b/332749288) Empty map so there are no back handlers and back can close + // customizer - val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation - - private fun destinationScenes( - isCustomizing: Boolean, - backScene: SceneKey?, - ): Map<UserAction, UserActionResult> { - return buildMap { - if (isCustomizing) { - // TODO(b/332749288) Empty map so there are no back handlers and back can close - // customizer - - // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade - // while customizing - } else { - put(Back, UserActionResult(backScene ?: Scenes.Shade)) - put(Swipe(SwipeDirection.Up), UserActionResult(backScene ?: Scenes.Shade)) - put( - Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up), - UserActionResult(SceneFamilies.Home), - ) + // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade + // while customizing + } else { + put(Back, UserActionResult(backScene)) + put(Swipe(SwipeDirection.Up), UserActionResult(backScene)) + put( + Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up), + UserActionResult(SceneFamilies.Home), + ) + } } } - } + + val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation private val footerActionsControllerInitialized = AtomicBoolean(false) diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt index 66fcbacb0b8a..e012f2cac1fb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt @@ -21,17 +21,13 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeAlignment import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn /** Models UI state and handles user input for the Quick Settings Shade scene. */ @SysUISingleton @@ -41,33 +37,22 @@ constructor( private val shadeInteractor: ShadeInteractor, val overlayShadeViewModel: OverlayShadeViewModel, val quickSettingsContainerViewModel: QuickSettingsContainerViewModel, - @Application applicationScope: CoroutineScope, ) { - val isEditing: StateFlow<Boolean> = quickSettingsContainerViewModel.editModeViewModel.isEditing - - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - isEditing - .map { editing -> destinations(editing) } - .stateIn( - applicationScope, - SharingStarted.WhileSubscribed(), - destinations(isEditing.value) - ) - - private fun destinations(editing: Boolean): Map<UserAction, UserActionResult> { - return buildMap { - put( - if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) { - Swipe.Up - } else { - Swipe.Down - }, - UserActionResult(SceneFamilies.Home) - ) - if (!editing) { - put(Back, UserActionResult(SceneFamilies.Home)) + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + quickSettingsContainerViewModel.editModeViewModel.isEditing.map { editing -> + buildMap { + put( + if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) { + Swipe.Up + } else { + Swipe.Down + }, + UserActionResult(SceneFamilies.Home) + ) + if (!editing) { + put(Back, UserActionResult(SceneFamilies.Home)) + } } } - } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt index 939d5bc6588e..c7190c3039a9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt @@ -19,7 +19,8 @@ package com.android.systemui.scene.shared.model import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult -import kotlinx.coroutines.flow.StateFlow +import com.android.systemui.activatable.Activatable +import kotlinx.coroutines.flow.Flow /** * Defines interface for classes that can describe a "scene". @@ -29,11 +30,13 @@ import kotlinx.coroutines.flow.StateFlow * based on either user action (for example, swiping down while on the lock screen scene may switch * to the shade scene). */ -interface Scene { +interface Scene : Activatable { /** Uniquely-identifying key for this scene. The key must be unique within its container. */ val key: SceneKey + override suspend fun activate() = Unit + /** * The mapping between [UserAction] and destination [UserActionResult]s. * @@ -54,5 +57,5 @@ interface Scene { * type is not currently active in the scene and should be ignored by the framework, while the * current scene is this one. */ - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> + val destinationScenes: Flow<Map<UserAction, UserActionResult>> } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt index c20d577d66a1..d31d6f4137c1 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -103,6 +103,7 @@ object SceneWindowRootViewBinder { windowInsets = windowInsets, sceneByKey = sortedSceneByKey, dataSourceDelegator = dataSourceDelegator, + containerConfig = containerConfig, ) .also { it.id = R.id.scene_container_root_composable } ) @@ -141,6 +142,7 @@ object SceneWindowRootViewBinder { windowInsets: StateFlow<WindowInsets?>, sceneByKey: Map<SceneKey, Scene>, dataSourceDelegator: SceneDataSourceDelegator, + containerConfig: SceneContainerConfig, ): View { return ComposeView(context).apply { setContent { @@ -153,6 +155,7 @@ object SceneWindowRootViewBinder { viewModel = viewModel, sceneByKey = sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, + initialSceneKey = containerConfig.initialSceneKey, dataSourceDelegator = dataSourceDelegator, ) } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt index 9f48ee9e5ecb..b739ffe1dc65 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt @@ -23,79 +23,61 @@ import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn @SysUISingleton class GoneSceneViewModel @Inject constructor( - @Application private val applicationScope: CoroutineScope, private val shadeInteractor: ShadeInteractor, ) { - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = - shadeInteractor.shadeMode - .map(::destinationScenes) - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = - destinationScenes( - shadeMode = shadeInteractor.shadeMode.value, - ) - ) + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = + shadeInteractor.shadeMode.map { shadeMode -> + buildMap { + if ( + shadeMode is ShadeMode.Single || + // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. + shadeMode is ShadeMode.Dual + ) { + if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) { + put( + Swipe( + pointerCount = 2, + fromSource = Edge.Bottom, + direction = SwipeDirection.Up, + ), + UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade) + ) + } else { + put( + Swipe( + pointerCount = 2, + fromSource = Edge.Top, + direction = SwipeDirection.Down, + ), + UserActionResult(SceneFamilies.QuickSettings) + ) + } + } - private fun destinationScenes( - shadeMode: ShadeMode, - ): Map<UserAction, UserActionResult> { - return buildMap { - if ( - shadeMode is ShadeMode.Single || - // TODO(b/338577208): Remove this once we add Dual Shade invocation zones. - shadeMode is ShadeMode.Dual - ) { if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) { - put( - Swipe( - pointerCount = 2, - fromSource = Edge.Bottom, - direction = SwipeDirection.Up, - ), - UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade) - ) + put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade)) } else { put( - Swipe( - pointerCount = 2, - fromSource = Edge.Top, - direction = SwipeDirection.Down, - ), - UserActionResult(SceneFamilies.QuickSettings) + Swipe.Down, + UserActionResult( + SceneFamilies.NotifShade, + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ) ) } } - - if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) { - put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade)) - } else { - put( - Swipe.Down, - UserActionResult( - SceneFamilies.NotifShade, - ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - ) - ) - } } - } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index c1caeed05cb6..91bfae31559a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -16,7 +16,6 @@ package com.android.systemui.shade; -import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; @@ -57,7 +56,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.StatusBarManager; import android.content.ContentResolver; import android.content.res.Resources; import android.database.ContentObserver; @@ -233,7 +231,6 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SplitShadeStateController; -import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.Compile; import com.android.systemui.util.Utils; @@ -705,7 +702,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump FalsingCollector falsingCollector, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, - StatusBarWindowStateController statusBarWindowStateController, NotificationShadeWindowController notificationShadeWindowController, DozeLog dozeLog, DozeParameters dozeParameters, @@ -913,7 +909,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mMediaDataManager = mediaDataManager; mTapAgainViewController = tapAgainViewController; mSysUiState = sysUiState; - statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; @@ -4882,16 +4877,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return mStatusBarStateListener; } - private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) { - if (state != WINDOW_STATE_SHOWING - && mStatusBarStateController.getState() == StatusBarState.SHADE) { - collapse( - false /* animate */, - false /* delayed */, - 1.0f /* speedUpFactor */); - } - } - /** Handles MotionEvents for the Shade. */ public final class TouchHandler implements View.OnTouchListener, Gefingerpoken { private long mLastTouchDownTime = -1L; diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt index f39ee9afd039..9e221d3d2341 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.domain.interactor +import com.android.app.tracing.FlowTracing.traceAsCounter import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -74,6 +75,7 @@ constructor( } } .distinctUntilChanged() + .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) override val qsExpansion: StateFlow<Float> = repository.qsExpansion diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt index b2142a515923..9617b542b427 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.domain.interactor +import com.android.app.tracing.FlowTracing.traceAsCounter import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.dagger.SysUISingleton @@ -46,6 +47,7 @@ constructor( ) : BaseShadeInteractor { override val shadeExpansion: StateFlow<Float> = sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade) + .traceAsCounter("panel_expansion") { (it * 100f).toInt() } .stateIn(scope, SharingStarted.Eagerly, 0f) private val sceneBasedQsExpansion = diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index 2b2aac64a7fa..f90dd3c2936b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -24,8 +24,8 @@ import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.systemui.activatable.Activatable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.qs.FooterActionsController import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel @@ -41,22 +41,23 @@ import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Models UI state and handles user input for the shade scene. */ @SysUISingleton class ShadeSceneViewModel @Inject constructor( - @Application private val applicationScope: CoroutineScope, val qsSceneAdapter: QSSceneAdapter, val shadeHeaderViewModel: ShadeHeaderViewModel, val brightnessMirrorViewModel: BrightnessMirrorViewModel, @@ -66,42 +67,61 @@ constructor( private val footerActionsController: FooterActionsController, private val sceneInteractor: SceneInteractor, private val unfoldTransitionInteractor: UnfoldTransitionInteractor, -) { - val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = +) : Activatable { + val destinationScenes: Flow<Map<UserAction, UserActionResult>> = combine( - shadeInteractor.shadeMode, - qsSceneAdapter.isCustomizerShowing, - ) { shadeMode, isCustomizerShowing -> - destinationScenes( - shadeMode = shadeMode, - isCustomizing = isCustomizerShowing, - ) + shadeInteractor.shadeMode, + qsSceneAdapter.isCustomizerShowing, + ) { shadeMode, isCustomizerShowing -> + buildMap { + if (!isCustomizerShowing) { + set( + Swipe(SwipeDirection.Up), + UserActionResult( + SceneFamilies.Home, + ToSplitShade.takeIf { shadeMode is ShadeMode.Split } + ) + ) + } + + // TODO(b/330200163) Add an else to be able to collapse the shade while customizing + if (shadeMode is ShadeMode.Single) { + set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings)) + } } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = - destinationScenes( - shadeMode = shadeInteractor.shadeMode.value, - isCustomizing = qsSceneAdapter.isCustomizerShowing.value, - ), - ) + } private val upDestinationSceneKey: Flow<SceneKey?> = destinationScenes.map { it[Swipe(SwipeDirection.Up)]?.toScene } + private val _isClickable = MutableStateFlow(false) /** Whether or not the shade container should be clickable. */ - val isClickable: StateFlow<Boolean> = - upDestinationSceneKey - .flatMapLatestConflated { key -> - key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null) + val isClickable: StateFlow<Boolean> = _isClickable.asStateFlow() + + /** + * Activates the view-model. + * + * Serves as an entrypoint to kick off coroutine work that the view-model requires in order to + * keep its state fresh and/or perform side-effects. + * + * Suspends the caller forever as it will keep doing work until canceled. + * + * **Must be invoked** when the scene becomes the current scene or when it becomes visible + * during a transition (the choice is the responsibility of the parent). Similarly, the work + * must be canceled when the scene stops being visible or the current scene. + */ + override suspend fun activate() { + coroutineScope { + launch { + upDestinationSceneKey + .flatMapLatestConflated { key -> + key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null) + } + .map { it == Scenes.Lockscreen } + .collectLatest { _isClickable.value = it } } - .map { it == Scenes.Lockscreen } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false - ) + } + } val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode @@ -132,24 +152,4 @@ constructor( } return footerActionsViewModelFactory.create(lifecycleOwner) } - - private fun destinationScenes( - shadeMode: ShadeMode, - isCustomizing: Boolean, - ): Map<UserAction, UserActionResult> { - return buildMap { - if (!isCustomizing) { - set( - Swipe(SwipeDirection.Up), - UserActionResult( - SceneFamilies.Home, - ToSplitShade.takeIf { shadeMode is ShadeMode.Split } - ) - ) - } // TODO(b/330200163) Add an else to be able to collapse the shade while customizing - if (shadeMode is ShadeMode.Single) { - set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings)) - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt index 59fd0ca4513e..0e3f77543eec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt @@ -105,6 +105,8 @@ constructor( } companion object { + // TODO(b/354930838): Use the icon from the call notification instead of hard-coding the + // icon to always be a phone. private val phoneIcon = Icon.Resource( com.android.internal.R.drawable.ic_phone, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt index a6605f652ff3..a621b2a02c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt @@ -18,12 +18,9 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.annotation.SuppressLint import android.app.NotificationManager -import android.os.UserHandle -import android.provider.Settings import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -43,23 +40,16 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_ONGOING import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN import com.android.systemui.util.asIndenting import com.android.systemui.util.printCollection -import com.android.systemui.util.settings.SecureSettings -import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import java.io.PrintWriter import javax.inject.Inject import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch /** @@ -73,12 +63,10 @@ import kotlinx.coroutines.launch class LockScreenMinimalismCoordinator @Inject constructor( - @Background private val bgDispatcher: CoroutineDispatcher, private val dumpManager: DumpManager, private val headsUpInteractor: HeadsUpNotificationInteractor, private val logger: LockScreenMinimalismCoordinatorLogger, @Application private val scope: CoroutineScope, - private val secureSettings: SecureSettings, private val seenNotificationsInteractor: SeenNotificationsInteractor, private val statusBarStateController: StatusBarStateController, private val shadeInteractor: ShadeInteractor, @@ -147,29 +135,7 @@ constructor( if (NotificationMinimalismPrototype.isEnabled) { return flowOf(true) } - return secureSettings - // emit whenever the setting has changed - .observerFlow( - UserHandle.USER_ALL, - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - ) - // perform a query immediately - .onStart { emit(Unit) } - // for each change, lookup the new value - .map { - secureSettings.getIntForUser( - name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - def = 0, - userHandle = UserHandle.USER_CURRENT, - ) == 1 - } - // don't emit anything if nothing has changed - .distinctUntilChanged() - // perform lookups on the bg thread pool - .flowOn(bgDispatcher) - // only track the most recent emission, if events are happening faster than they can be - // consumed - .conflate() + return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled() } private suspend fun trackUnseenFilterSettingChanges() { @@ -177,6 +143,7 @@ constructor( // update local field and invalidate if necessary if (isSettingEnabled != unseenFilterEnabled) { unseenFilterEnabled = isSettingEnabled + unseenNotifications.clear() unseenNotifPromoter.invalidateList("unseen setting changed") } // if the setting is enabled, then start tracking and filtering unseen notifications @@ -190,21 +157,21 @@ constructor( private val collectionListener = object : NotifCollectionListener { override fun onEntryAdded(entry: NotificationEntry) { - if (!isShadeVisible) { + if (unseenFilterEnabled && !isShadeVisible) { logger.logUnseenAdded(entry.key) unseenNotifications.add(entry) } } override fun onEntryUpdated(entry: NotificationEntry) { - if (!isShadeVisible) { + if (unseenFilterEnabled && !isShadeVisible) { logger.logUnseenUpdated(entry.key) unseenNotifications.add(entry) } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { - if (unseenNotifications.remove(entry)) { + if (unseenFilterEnabled && unseenNotifications.remove(entry)) { logger.logUnseenRemoved(entry.key) } } @@ -212,6 +179,7 @@ constructor( private fun pickOutTopUnseenNotifs(list: List<ListEntry>) { if (NotificationMinimalismPrototype.isUnexpectedlyInLegacyMode()) return + if (!unseenFilterEnabled) return // Only ever elevate a top unseen notification on keyguard, not even locked shade if (statusBarStateController.state != StatusBarState.KEYGUARD) { seenNotificationsInteractor.setTopOngoingNotification(null) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt index 5b25b117c761..bfea2ba6b839 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt @@ -17,17 +17,16 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.annotation.SuppressLint -import android.os.UserHandle -import android.provider.Settings import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.expansionChanges import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -41,12 +40,9 @@ import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.asIndenting import com.android.systemui.util.indentIfPossible -import com.android.systemui.util.settings.SecureSettings -import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import java.io.PrintWriter import javax.inject.Inject import kotlin.time.Duration.Companion.seconds -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -54,13 +50,10 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import kotlinx.coroutines.yield @@ -77,16 +70,15 @@ import kotlinx.coroutines.yield class OriginalUnseenKeyguardCoordinator @Inject constructor( - @Background private val bgDispatcher: CoroutineDispatcher, private val dumpManager: DumpManager, private val headsUpManager: HeadsUpManager, private val keyguardRepository: KeyguardRepository, private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val logger: KeyguardCoordinatorLogger, @Application private val scope: CoroutineScope, - private val secureSettings: SecureSettings, private val seenNotificationsInteractor: SeenNotificationsInteractor, private val statusBarStateController: StatusBarStateController, + private val sceneInteractor: SceneInteractor, ) : Coordinator, Dumpable { private val unseenNotifications = mutableSetOf<NotificationEntry>() @@ -106,12 +98,15 @@ constructor( // Whether or not keyguard is visible (or occluded). @Suppress("DEPRECATION") val isKeyguardPresentFlow: Flow<Boolean> = - keyguardTransitionInteractor - .transitionValue( - scene = Scenes.Gone, - stateWithoutSceneContainer = KeyguardState.GONE, - ) - .map { it == 0f } + if (SceneContainerFlag.isEnabled) { + sceneInteractor.transitionState.map { + !it.isTransitioning(to = Scenes.Gone) && !it.isIdle(Scenes.Gone) + } + } else { + keyguardTransitionInteractor.transitions.map { step -> + step.to != KeyguardState.GONE + } + } .distinctUntilChanged() .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) } @@ -262,29 +257,7 @@ constructor( // TODO(b/330387368): should this really just be turned off? If so, hide the setting. return flowOf(false) } - return secureSettings - // emit whenever the setting has changed - .observerFlow( - UserHandle.USER_ALL, - Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - ) - // perform a query immediately - .onStart { emit(Unit) } - // for each change, lookup the new value - .map { - secureSettings.getIntForUser( - name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, - def = 0, - userHandle = UserHandle.USER_CURRENT, - ) == 1 - } - // don't emit anything if nothing has changed - .distinctUntilChanged() - // perform lookups on the bg thread pool - .flowOn(bgDispatcher) - // only track the most recent emission, if events are happening faster than they can be - // consumed - .conflate() + return seenNotificationsInteractor.isLockScreenShowOnlyUnseenNotificationsEnabled() } private suspend fun trackUnseenFilterSettingChanges() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 0f6f03ae8334..3a2f95e5ebc4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -263,7 +263,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable { private boolean isReorderingAllowed() { final boolean sleepyAndDozed = mFullyDozed && mSleepy; - final boolean stackShowing = mPanelExpanded || mLockscreenShowing; + final boolean stackShowing = mPanelExpanded + || (SceneContainerFlag.isEnabled() && mLockscreenShowing); return (sleepyAndDozed || !stackShowing || mCommunalShowing) && !mPulsing; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt index 948a3c2f65b0..90a05ef99586 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt @@ -16,21 +16,35 @@ package com.android.systemui.statusbar.notification.domain.interactor +import android.os.UserHandle +import android.provider.Settings import android.util.IndentingPrintWriter import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.util.printSection +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart /** Interactor for business logic associated with the notification stack. */ @SysUISingleton class SeenNotificationsInteractor @Inject constructor( + @Background private val bgDispatcher: CoroutineDispatcher, private val notificationListRepository: ActiveNotificationListRepository, + private val secureSettings: SecureSettings, ) { /** Are any already-seen notifications currently filtered out of the shade? */ val hasFilteredOutSeenNotifications: StateFlow<Boolean> = @@ -81,4 +95,29 @@ constructor( ) } } + + fun isLockScreenShowOnlyUnseenNotificationsEnabled(): Flow<Boolean> = + secureSettings + // emit whenever the setting has changed + .observerFlow( + UserHandle.USER_ALL, + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + ) + // perform a query immediately + .onStart { emit(Unit) } + // for each change, lookup the new value + .map { + secureSettings.getIntForUser( + name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + def = 0, + userHandle = UserHandle.USER_CURRENT, + ) == 1 + } + // don't emit anything if nothing has changed + .distinctUntilChanged() + // perform lookups on the bg thread pool + .flowOn(bgDispatcher) + // only track the most recent emission, if events are happening faster than they can be + // consumed + .conflate() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt index 331d3cc4c21b..a39c48772f1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt @@ -123,6 +123,8 @@ constructor( // Construct the status bar icon view. val sbIcon = iconBuilder.createIconView(entry) sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE + val sbChipIcon = iconBuilder.createIconView(entry) + sbChipIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE // Construct the shelf icon view. val shelfIcon = iconBuilder.createIconView(entry) @@ -139,9 +141,17 @@ constructor( try { setIcon(entry, normalIconDescriptor, sbIcon) + setIcon(entry, normalIconDescriptor, sbChipIcon) setIcon(entry, sensitiveIconDescriptor, shelfIcon) setIcon(entry, sensitiveIconDescriptor, aodIcon) - entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons) + entry.icons = + IconPack.buildPack( + sbIcon, + sbChipIcon, + shelfIcon, + aodIcon, + entry.icons, + ) } catch (e: InflationException) { entry.icons = IconPack.buildEmptyPack(entry.icons) throw e diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java index d029ce722af9..82bb5ef7a448 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java @@ -29,6 +29,7 @@ public final class IconPack { private final boolean mAreIconsAvailable; @Nullable private final StatusBarIconView mStatusBarIcon; + @Nullable private final StatusBarIconView mStatusBarChipIcon; @Nullable private final StatusBarIconView mShelfIcon; @Nullable private final StatusBarIconView mAodIcon; @@ -43,7 +44,7 @@ public final class IconPack { * haven't been inflated yet or there was an error while inflating them). */ public static IconPack buildEmptyPack(@Nullable IconPack fromSource) { - return new IconPack(false, null, null, null, fromSource); + return new IconPack(false, null, null, null, null, fromSource); } /** @@ -51,20 +52,24 @@ public final class IconPack { */ public static IconPack buildPack( @NonNull StatusBarIconView statusBarIcon, + @NonNull StatusBarIconView statusBarChipIcon, @NonNull StatusBarIconView shelfIcon, @NonNull StatusBarIconView aodIcon, @Nullable IconPack source) { - return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, source); + return new IconPack( + true, statusBarIcon, statusBarChipIcon, shelfIcon, aodIcon, source); } private IconPack( boolean areIconsAvailable, @Nullable StatusBarIconView statusBarIcon, + @Nullable StatusBarIconView statusBarChipIcon, @Nullable StatusBarIconView shelfIcon, @Nullable StatusBarIconView aodIcon, @Nullable IconPack source) { mAreIconsAvailable = areIconsAvailable; mStatusBarIcon = statusBarIcon; + mStatusBarChipIcon = statusBarChipIcon; mShelfIcon = shelfIcon; mAodIcon = aodIcon; if (source != null) { @@ -79,6 +84,17 @@ public final class IconPack { } /** + * The version of the notification icon that appears inside a chip within the status bar. + * + * Separate from {@link #getStatusBarIcon()} so that we don't have to worry about detaching and + * re-attaching the same view when the chip appears and hides. + */ + @Nullable + public StatusBarIconView getStatusBarChipIcon() { + return mStatusBarChipIcon; + } + + /** * The version of the icon that appears in the "shelf" at the bottom of the notification shade. * In general, this icon also appears somewhere on the notification and is "sucked" into the * shelf as the scrolls beyond it. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index e04e0facc766..9d09595d2c23 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -389,6 +389,7 @@ class AvalancheSuppressor( .setTicker(titleStr) .setContentTitle(titleStr) .setContentText(textStr) + .setStyle(Notification.BigTextStyle().bigText(textStr)) .setSmallIcon(com.android.systemui.res.R.drawable.ic_settings) .setCategory(Notification.CATEGORY_SYSTEM) .setTimeoutAfter(/* one day in ms */ 24 * 60 * 60 * 1000L) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index 57e52b7dc2ad..2ba79a8612bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -112,14 +112,22 @@ constructor( private operator fun SceneKey.contains(scene: SceneKey) = sceneInteractor.isSceneInFamily(scene, this) + private val qsAllowsClipping: Flow<Boolean> = + combine(shadeInteractor.shadeMode, shadeInteractor.qsExpansion) { shadeMode, qsExpansion -> + qsExpansion < 0.5f || shadeMode != ShadeMode.Single + } + .distinctUntilChanged() + /** The bounds of the notification stack in the current scene. */ private val shadeScrimClipping: Flow<ShadeScrimClipping?> = combine( + qsAllowsClipping, stackAppearanceInteractor.shadeScrimBounds, stackAppearanceInteractor.shadeScrimRounding, - ) { bounds, rounding -> - bounds?.let { ShadeScrimClipping(it, rounding) } + ) { qsAllowsClipping, bounds, rounding -> + bounds?.takeIf { qsAllowsClipping }?.let { ShadeScrimClipping(it, rounding) } } + .distinctUntilChanged() .dumpWhileCollecting("stackClipping") fun shadeScrimShape( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 3898088eee9e..2ea8da8ed908 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -24,8 +24,12 @@ import android.app.PendingIntent import android.app.UidObserver import android.content.Context import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor +import com.android.settingslib.Utils import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.Flags @@ -38,6 +42,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore @@ -115,6 +120,7 @@ constructor( Notification.EXTRA_CALL_TYPE, -1 ) == CALL_TYPE_ONGOING, + entry.icons.statusBarChipIcon, statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false ) if (newOngoingCallInfo == callNotificationInfo) { @@ -176,8 +182,7 @@ constructor( fun setChipView(chipView: View) { tearDownChipView() this.chipView = chipView - val backgroundView: ChipBackgroundContainer? = - chipView.findViewById(R.id.ongoing_activity_chip_background) + val backgroundView: ChipBackgroundContainer? = chipView.getBackgroundView() backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight } if (hasOngoingCall()) { updateChip() @@ -247,6 +252,8 @@ constructor( timeView.setShouldHideText(true) timeView.stop() } + + updateChipIcon(currentCallNotificationInfo, currentChipView) updateChipClickListener() } @@ -270,6 +277,38 @@ constructor( } } + private fun updateChipIcon(callInfo: CallNotificationInfo, currentChipView: View) { + val backgroundView = currentChipView.getBackgroundView() ?: return + backgroundView.removeView(currentChipView.getIconView()) + + val iconView = callInfo.iconView ?: return + with(iconView) { + id = ICON_ID + imageTintList = Utils.getColorAttr(context, android.R.attr.colorPrimary) + // TODO(b/354930838): Update the content description to not include "phone". + contentDescription = + context.resources.getString(R.string.ongoing_phone_call_content_description) + } + + val currentParent = iconView.parent as? ViewGroup + // If we're reinflating the view, we may need to detach the icon view from the + // old chip before we reattach it to the new one. + // See also: NotificationIconContainerViewBinder#bindIcons. + if (currentParent != null && currentParent != backgroundView) { + currentParent.removeView(iconView) + currentParent.removeTransientView(iconView) + } + backgroundView.addView(iconView, /* index= */ 0, generateIconLayoutParams()) + } + + private fun generateIconLayoutParams(): FrameLayout.LayoutParams { + return FrameLayout.LayoutParams(iconSize, iconSize) + } + + private val iconSize: Int + get() = + context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_notif_icon_size) + private fun updateChipClickListener() { if (Flags.statusBarScreenSharingChips()) { return @@ -279,8 +318,7 @@ constructor( return } val currentChipView = chipView - val backgroundView = - currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background) + val backgroundView = currentChipView?.getBackgroundView() val intent = callNotificationInfo?.intent if (currentChipView != null && backgroundView != null && intent != null) { currentChipView.setOnClickListener { @@ -332,6 +370,14 @@ constructor( return this.findViewById(R.id.ongoing_activity_chip_time) } + private fun View.getBackgroundView(): ChipBackgroundContainer? { + return this.findViewById(R.id.ongoing_activity_chip_background) + } + + private fun View.getIconView(): View? { + return this.findViewById(ICON_ID) + } + /** * If there's an active ongoing call, then we will force the status bar to always show, even if * the user is in immersive mode. However, we also want to give users the ability to swipe away @@ -359,6 +405,8 @@ constructor( val uid: Int, /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ val isOngoing: Boolean, + /** The view that contains the icon to display in the chip. */ + val iconView: StatusBarIconView?, /** True if the user has swiped away the status bar while in this phone call. */ val statusBarSwipedAway: Boolean ) { @@ -464,6 +512,10 @@ constructor( } } } + + companion object { + @IdRes private val ICON_ID = R.id.ongoing_activity_chip_icon + } } private fun isCallNotification(entry: NotificationEntry): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt index 91bfdff1095e..3b392c84420a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt @@ -50,14 +50,14 @@ fun ModeTile(viewModel: ModeTileViewModel) { Surface( color = tileColor, shape = RoundedCornerShape(16.dp), - modifier = - Modifier.combinedClickable( - onClick = viewModel.onClick, - onLongClick = viewModel.onLongClick - ), ) { Row( - modifier = Modifier.padding(20.dp), + modifier = + Modifier.combinedClickable( + onClick = viewModel.onClick, + onLongClick = viewModel.onLongClick + ) + .padding(20.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy( diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java index f3b9cc12713c..9ae66749aa0a 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java @@ -40,14 +40,11 @@ import javax.inject.Inject; public class ToastFactory implements Dumpable { // only one ToastPlugin can be connected at a time. private ToastPlugin mPlugin; - private final LayoutInflater mLayoutInflater; @Inject public ToastFactory( - LayoutInflater layoutInflater, PluginManager pluginManager, DumpManager dumpManager) { - mLayoutInflater = layoutInflater; dumpManager.registerDumpable("ToastFactory", this); pluginManager.addPluginListener( new PluginListener<ToastPlugin>() { @@ -70,11 +67,12 @@ public class ToastFactory implements Dumpable { */ public SystemUIToast createToast(Context context, CharSequence text, String packageName, int userId, int orientation) { + LayoutInflater layoutInflater = LayoutInflater.from(context); if (isPluginAvailable()) { - return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text, + return new SystemUIToast(layoutInflater, context, text, mPlugin.createToast(text, packageName, userId), packageName, userId, orientation); } - return new SystemUIToast(mLayoutInflater, context, text, packageName, userId, + return new SystemUIToast(layoutInflater, context, text, packageName, userId, orientation); } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 85a455d23d49..bbfa32b623ef 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -128,7 +128,7 @@ public class ToastUI implements return; } Context displayContext = context.createDisplayContext(display); - mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName, + mToast = mToastFactory.createToast(displayContext /* sysuiContext */, text, packageName, userHandle.getIdentifier(), mOrientation); if (mToast.getInAnimation() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt new file mode 100644 index 000000000000..8ba8db498a36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.touchpad.tutorial + +import android.app.Activity +import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +interface TouchpadKeyboardTutorialModule { + + @Binds + @IntoMap + @ClassKey(TouchpadTutorialActivity::class) + fun activity(impl: TouchpadTutorialActivity): Activity +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt index 8721b69f438d..37833d886aca 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt @@ -142,6 +142,7 @@ private fun GestureTutorialContent( ) Spacer(modifier = Modifier.width(76.dp)) TutorialAnimation( + gestureDone, screenColors.animationProperties, modifier = Modifier.weight(1f).padding(top = 8.dp) ) @@ -173,14 +174,18 @@ fun TutorialDescription( } @Composable -fun TutorialAnimation(animationProperties: LottieDynamicProperties, modifier: Modifier = Modifier) { +fun TutorialAnimation( + gestureDone: Boolean, + animationProperties: LottieDynamicProperties, + modifier: Modifier = Modifier +) { Column(modifier = modifier.fillMaxWidth()) { - val composition by - rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.trackpad_back_edu)) + val resId = if (gestureDone) R.raw.trackpad_back_success else R.raw.trackpad_back_edu + val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(resId)) val progress by animateLottieCompositionAsState( composition, - iterations = LottieConstants.IterateForever + iterations = if (gestureDone) 1 else LottieConstants.IterateForever ) LottieAnimation( composition = composition, diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt index a1fe0f082b56..6c4a730dc637 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt @@ -27,6 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.FakeUserTracker import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable @@ -67,6 +68,7 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { @Mock private lateinit var systemProperties: SystemPropertiesHelper @Mock private lateinit var resources: Resources @Mock private lateinit var restarter: Restarter + private val userTracker = FakeUserTracker() private val flagMap = mutableMapOf<String, Flag<*>>() private lateinit var broadcastReceiver: BroadcastReceiver private lateinit var clearCacheAction: Consumer<String> @@ -78,7 +80,6 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA) flagMap.put(releasedFlagB.name, releasedFlagB) mFeatureFlagsClassicDebug = @@ -90,7 +91,8 @@ class FeatureFlagsClassicDebugTest : SysuiTestCase() { resources, serverFlagReader, flagMap, - restarter + restarter, + userTracker ) mFeatureFlagsClassicDebug.init() verify(flagManager).onSettingsChangedAction = any() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index f726aae318df..e251ab50e3c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat import com.android.systemui.util.mockito.whenever +import java.util.function.Predicate import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -46,7 +47,6 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations import org.mockito.kotlin.clearInvocations -import java.util.function.Predicate @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -54,70 +54,134 @@ import java.util.function.Predicate class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController - @Mock - private lateinit var windowManager: WindowManager - @Mock - private lateinit var keyguardViewMediator: KeyguardViewMediator - @Mock - private lateinit var keyguardStateController: KeyguardStateController - @Mock - private lateinit var keyguardViewController: KeyguardViewController - @Mock - private lateinit var featureFlags: FeatureFlags - @Mock - private lateinit var biometricUnlockController: BiometricUnlockController - @Mock - private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier - @Mock - private lateinit var statusBarStateController: SysuiStatusBarStateController - @Mock - private lateinit var notificationShadeWindowController: NotificationShadeWindowController - @Mock - private lateinit var powerManager: PowerManager - @Mock - private lateinit var wallpaperManager: WallpaperManager + @Mock private lateinit var windowManager: WindowManager + @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var keyguardViewController: KeyguardViewController + @Mock private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var biometricUnlockController: BiometricUnlockController + @Mock private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier + @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock private lateinit var powerManager: PowerManager + @Mock private lateinit var wallpaperManager: WallpaperManager @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub private var surfaceControl1 = mock(SurfaceControl::class.java) - private var remoteTarget1 = RemoteAnimationTarget( - 0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(), - mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(), - mock(ActivityManager.RunningTaskInfo::class.java), false) + private var remoteTarget1 = + RemoteAnimationTarget( + 0 /* taskId */, + 0, + surfaceControl1, + false, + Rect(), + Rect(), + 0, + Point(), + Rect(), + Rect(), + mock(WindowConfiguration::class.java), + false, + surfaceControl1, + Rect(), + mock(ActivityManager.RunningTaskInfo::class.java), + false + ) private var surfaceControl2 = mock(SurfaceControl::class.java) - private var remoteTarget2 = RemoteAnimationTarget( - 1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(), - mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(), - mock(ActivityManager.RunningTaskInfo::class.java), false) + private var remoteTarget2 = + RemoteAnimationTarget( + 1 /* taskId */, + 0, + surfaceControl2, + false, + Rect(), + Rect(), + 0, + Point(), + Rect(), + Rect(), + mock(WindowConfiguration::class.java), + false, + surfaceControl2, + Rect(), + mock(ActivityManager.RunningTaskInfo::class.java), + false + ) private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget> private var surfaceControlWp = mock(SurfaceControl::class.java) - private var wallpaperTarget = RemoteAnimationTarget( - 2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(), - mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(), - mock(ActivityManager.RunningTaskInfo::class.java), false) + private var wallpaperTarget = + RemoteAnimationTarget( + 2 /* taskId */, + 0, + surfaceControlWp, + false, + Rect(), + Rect(), + 0, + Point(), + Rect(), + Rect(), + mock(WindowConfiguration::class.java), + false, + surfaceControlWp, + Rect(), + mock(ActivityManager.RunningTaskInfo::class.java), + false + ) private lateinit var wallpaperTargets: Array<RemoteAnimationTarget> private var surfaceControlLockWp = mock(SurfaceControl::class.java) - private var lockWallpaperTarget = RemoteAnimationTarget( - 3 /* taskId */, 0, surfaceControlLockWp, false, Rect(), Rect(), 0, Point(), Rect(), - Rect(), mock(WindowConfiguration::class.java), false, surfaceControlLockWp, - Rect(), mock(ActivityManager.RunningTaskInfo::class.java), false) + private var lockWallpaperTarget = + RemoteAnimationTarget( + 3 /* taskId */, + 0, + surfaceControlLockWp, + false, + Rect(), + Rect(), + 0, + Point(), + Rect(), + Rect(), + mock(WindowConfiguration::class.java), + false, + surfaceControlLockWp, + Rect(), + mock(ActivityManager.RunningTaskInfo::class.java), + false + ) private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget> + private var shouldPerformSmartspaceTransition = false @Before fun setUp() { MockitoAnnotations.initMocks(this) - keyguardUnlockAnimationController = KeyguardUnlockAnimationController( - windowManager, context.resources, - keyguardStateController, { keyguardViewMediator }, keyguardViewController, - featureFlags, { biometricUnlockController }, statusBarStateController, - notificationShadeWindowController, powerManager, wallpaperManager - ) + keyguardUnlockAnimationController = + object : + KeyguardUnlockAnimationController( + windowManager, + context.resources, + keyguardStateController, + { keyguardViewMediator }, + keyguardViewController, + featureFlags, + { biometricUnlockController }, + statusBarStateController, + notificationShadeWindowController, + powerManager, + wallpaperManager + ) { + override fun shouldPerformSmartspaceTransition(): Boolean = + shouldPerformSmartspaceTransition + } keyguardUnlockAnimationController.setLauncherUnlockController( - "", launcherUnlockAnimationController) + "", + launcherUnlockAnimationController + ) whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) whenever(powerManager.isInteractive).thenReturn(true) @@ -159,8 +223,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { ) val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(1)).scheduleApply( - captorSb.capture { sp -> sp.surface == surfaceControl1 }) + verify(surfaceTransactionApplier, times(1)) + .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 }) val params = captorSb.getLastValue() @@ -171,15 +235,13 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { // Also expect we've immediately asked the keyguard view mediator to finish the remote // animation. - verify(keyguardViewMediator, times(1)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) + verify(keyguardViewMediator, times(1)) + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) verifyNoMoreInteractions(surfaceTransactionApplier) } - /** - * If we are not wake and unlocking, we expect the unlock animation to play normally. - */ + /** If we are not wake and unlocking, we expect the unlock animation to play normally. */ @Test fun surfaceAnimation_ifNotWakeAndUnlocking() { whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false) @@ -193,18 +255,18 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { ) // Since the animation is running, we should not have finished the remote animation. - verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) + verify(keyguardViewMediator, times(0)) + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) } @Test fun onWakeAndUnlock_notifiesListenerWithTrue() { whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - whenever(biometricUnlockController.mode).thenReturn( - BiometricUnlockController.MODE_WAKE_AND_UNLOCK) + whenever(biometricUnlockController.mode) + .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK) - val listener = mock( - KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) + val listener = + mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( @@ -221,11 +283,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { @Test fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() { whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) - whenever(biometricUnlockController.mode).thenReturn( - BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) + whenever(biometricUnlockController.mode) + .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) - val listener = mock( - KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) + val listener = + mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( @@ -269,8 +331,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and * lifted their finger while we were requesting the surface be made visible. * - * In this case, we should verify that we are playing the canned unlock animation and not - * simply fading in the surface. + * In this case, we should verify that we are playing the canned unlock animation and not simply + * fading in the surface. */ @Test fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() { @@ -293,8 +355,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock, * long press on the lock icon, etc). * - * In this case, we should verify that we are playing the canned unlock animation and not - * simply fading in the surface. + * In this case, we should verify that we are playing the canned unlock animation and not simply + * fading in the surface. */ @Test fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() { @@ -332,11 +394,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( - remoteAnimationTargets, - wallpaperTargets, - arrayOf(), - 0 /* startTime */, - false /* requestedShowSurfaceBehindKeyguard */ + remoteAnimationTargets, + wallpaperTargets, + arrayOf(), + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ ) assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) @@ -353,11 +415,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { var lastFadeOutAlpha = -1f keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( - arrayOf(remoteTarget1, remoteTarget2), - wallpaperTargets, - lockWallpaperTargets, - 0 /* startTime */, - false /* requestedShowSurfaceBehindKeyguard */ + arrayOf(remoteTarget1, remoteTarget2), + wallpaperTargets, + lockWallpaperTargets, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ ) for (i in 0..10) { @@ -367,19 +429,22 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(amount) val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(2)).scheduleApply( + verify(surfaceTransactionApplier, times(2)) + .scheduleApply( captorSb.capture { sp -> - sp.surface == surfaceControlWp || sp.surface == surfaceControlLockWp }) + sp.surface == surfaceControlWp || sp.surface == surfaceControlLockWp + } + ) val fadeInAlpha = captorSb.getLastValue { it.surface == surfaceControlWp }.alpha val fadeOutAlpha = captorSb.getLastValue { it.surface == surfaceControlLockWp }.alpha if (amount == 0f) { - assertTrue (fadeInAlpha == 0f) - assertTrue (fadeOutAlpha == 1f) + assertTrue(fadeInAlpha == 0f) + assertTrue(fadeOutAlpha == 1f) } else if (amount == 1f) { - assertTrue (fadeInAlpha == 1f) - assertTrue (fadeOutAlpha == 0f) + assertTrue(fadeInAlpha == 1f) + assertTrue(fadeOutAlpha == 0f) } else { assertTrue(fadeInAlpha >= lastFadeInAlpha) assertTrue(fadeOutAlpha <= lastFadeOutAlpha) @@ -389,18 +454,16 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { } } - /** - * If we are not wake and unlocking, we expect the unlock animation to play normally. - */ + /** If we are not wake and unlocking, we expect the unlock animation to play normally. */ @Test @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun surfaceAnimation_multipleTargets() { keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( - arrayOf(remoteTarget1, remoteTarget2), - wallpaperTargets, - arrayOf(), - 0 /* startTime */, - false /* requestedShowSurfaceBehindKeyguard */ + arrayOf(remoteTarget1, remoteTarget2), + wallpaperTargets, + arrayOf(), + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ ) // Cancel the animator so we can verify only the setSurfaceBehind call below. @@ -412,12 +475,18 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f) val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb - .capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 }) + verify(surfaceTransactionApplier, times(2)) + .scheduleApply( + captorSb.capture { sp -> + sp.surface == surfaceControl1 || sp.surface == surfaceControl2 + } + ) val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(1).description( - "WallpaperSurface was expected to receive scheduleApply once" - )).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp}) + verify( + surfaceTransactionApplier, + times(1).description("WallpaperSurface was expected to receive scheduleApply once") + ) + .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp }) val allParams = captorSb.getAllValues() @@ -432,8 +501,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { assertTrue(remainingTargets.isEmpty()) // Since the animation is running, we should not have finished the remote animation. - verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation( - false /* cancelled */) + verify(keyguardViewMediator, times(0)) + .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */) } @Test @@ -442,11 +511,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { whenever(powerManager.isInteractive).thenReturn(false) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( - remoteAnimationTargets, - wallpaperTargets, - arrayOf(), - 0 /* startTime */, - false /* requestedShowSurfaceBehindKeyguard */ + remoteAnimationTargets, + wallpaperTargets, + arrayOf(), + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ ) // Cancel the animator so we can verify only the setSurfaceBehind call below. @@ -457,12 +526,14 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets) val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(1)).scheduleApply( - captorSb.capture { sp -> sp.surface == surfaceControl1}) + verify(surfaceTransactionApplier, times(1)) + .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 }) val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " + - "received scheduleApply")).scheduleApply( - captorWp.capture { sp -> sp.surface == surfaceControlWp }) + verify( + surfaceTransactionApplier, + atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply") + ) + .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp }) val params = captorSb.getLastValue() @@ -479,11 +550,11 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { whenever(powerManager.isInteractive).thenReturn(true) keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( - remoteAnimationTargets, - wallpaperTargets, - arrayOf(), - 0 /* startTime */, - false /* requestedShowSurfaceBehindKeyguard */ + remoteAnimationTargets, + wallpaperTargets, + arrayOf(), + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ ) // Stop the animator - we just want to test whether the override is not applied. @@ -494,24 +565,31 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets) val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, times(1)).scheduleApply( - captorSb.capture { sp -> sp.surface == surfaceControl1 }) + verify(surfaceTransactionApplier, times(1)) + .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 }) val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>() - verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " + - "received scheduleApply")).scheduleApply( - captorWp.capture { sp -> sp.surface == surfaceControlWp }) + verify( + surfaceTransactionApplier, + atLeastOnce().description("Wallpaper surface has not " + "received scheduleApply") + ) + .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp }) val params = captorSb.getLastValue() assertEquals(1f, params.alpha) assertTrue(params.matrix.isIdentity) - assertEquals("Wallpaper surface was expected to have opacity 1", - 1f, captorWp.getLastValue().alpha) + assertEquals( + "Wallpaper surface was expected to have opacity 1", + 1f, + captorWp.getLastValue().alpha + ) verifyNoMoreInteractions(surfaceTransactionApplier) } @Test - fun unlockToLauncherWithInWindowAnimations_ssViewIsVisible() { + fun unlockToLauncherWithInWindowAnimations_ssViewInVisible_whenPerformSSTransition() { + shouldPerformSmartspaceTransition = true + val mockLockscreenSmartspaceView = mock(View::class.java) whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE) keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView @@ -522,6 +600,19 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { } @Test + fun unlockToLauncherWithInWindowAnimations_ssViewVisible_whenNotPerformSSTransition() { + shouldPerformSmartspaceTransition = false + + val mockLockscreenSmartspaceView = mock(View::class.java) + whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE) + keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView + + keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations() + + verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE + } + + @Test fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() { val mockLockscreenSmartspaceView = mock(View::class.java) whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE) @@ -591,7 +682,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { private var allArgs: MutableList<T> = mutableListOf() fun capture(predicate: Predicate<T>): T { - return argThat{x: T -> + return argThat { x: T -> if (predicate.test(x)) { allArgs.add(x) return@argThat true diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt index a310520763e3..32d059b2f037 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt @@ -33,6 +33,8 @@ import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver +import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -78,6 +80,8 @@ class KeyguardDismissActionInteractorTest : SysuiTestCase() { applicationScope = testScope.backgroundScope, sceneInteractor = kosmos.sceneInteractor, deviceEntryInteractor = kosmos.deviceEntryInteractor, + quickSettingsSceneFamilyResolver = kosmos.quickSettingsSceneFamilyResolver, + notifShadeSceneFamilyResolver = kosmos.notifShadeSceneFamilyResolver, ) } 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 a5c4bcd46a1f..56fb43d056a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -698,7 +698,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mFalsingManager, new FalsingCollectorFake(), mKeyguardStateController, mStatusBarStateController, - mStatusBarWindowStateController, mNotificationShadeWindowController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mAccessibilityManager, 0, mUpdateMonitor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt deleted file mode 100644 index 39085295fbac..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - * - */ - -package com.android.systemui.statusbar.notification.domain.interactor - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runTest -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class SeenNotificationsInteractorTest : SysuiTestCase() { - - private val repository = ActiveNotificationListRepository() - private val underTest = SeenNotificationsInteractor(repository) - - @Test - fun testNoFilteredOutSeenNotifications() = runTest { - val hasFilteredOutSeenNotifications by - collectLastValue(underTest.hasFilteredOutSeenNotifications) - - underTest.setHasFilteredOutSeenNotifications(false) - - assertThat(hasFilteredOutSeenNotifications).isFalse() - } - - @Test - fun testHasFilteredOutSeenNotifications() = runTest { - val hasFilteredOutSeenNotifications by - collectLastValue(underTest.hasFilteredOutSeenNotifications) - - underTest.setHasFilteredOutSeenNotifications(true) - - assertThat(hasFilteredOutSeenNotifications).isTrue() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 1060b62f071f..c36a046532bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -91,7 +91,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.NotifStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; -import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository; import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.init.NotificationsController; @@ -188,12 +187,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; - - private final ActiveNotificationListRepository mActiveNotificationsRepository = - new ActiveNotificationListRepository(); - private final SeenNotificationsInteractor mSeenNotificationsInteractor = - new SeenNotificationsInteractor(mActiveNotificationsRepository); + mKosmos.getSeenNotificationsInteractor(); private NotificationStackScrollLayoutController mController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt index fd2dead02c6c..e670884eff17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt @@ -334,6 +334,7 @@ class PhoneStatusBarViewTest : SysuiTestCase() { /* typeVisibilityMap = */ booleanArrayOf(), /* isRound = */ false, /* forceConsumingTypes = */ 0, + /* forceConsumingCaptionBar = */ false, /* suppressScrimTypes = */ 0, /* displayCutout = */ DisplayCutout.NO_CUTOUT, /* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index c4371fd6303b..8f49ba3b2154 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -38,8 +38,10 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler +import com.android.systemui.statusbar.notification.icon.IconPack import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -254,6 +256,139 @@ class OngoingCallControllerTest : SysuiTestCase() { .isGreaterThan(0) } + @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + fun onEntryUpdated_notifIconsNotSet_noIconInChip() { + val notification = createOngoingCallNotifEntry() + + notifCollectionListener.onEntryUpdated(notification) + + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)).isNull() + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + fun onEntryUpdated_notifIconsSetToNull_noIconInChip() { + val notification = createOngoingCallNotifEntry() + notification.icons = IconPack.buildEmptyPack(/* fromSource= */ null) + + notifCollectionListener.onEntryUpdated(notification) + + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)).isNull() + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + fun onEntryUpdated_notifIconsIncluded_statusBarChipIconUsed() { + val notification = createOngoingCallNotifEntry() + + val statusBarChipIconView = + StatusBarIconView( + context, + /* slot= */ "OngoingCallControllerTest", + notification.sbn, + ) + notification.icons = + IconPack.buildPack( + /* statusBarIcon= */ mock(StatusBarIconView::class.java), + statusBarChipIconView, + /* shelfIcon= */ mock(StatusBarIconView::class.java), + /* aodIcon= */ mock(StatusBarIconView::class.java), + /* source= */ null, + ) + + notifCollectionListener.onEntryUpdated(notification) + + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)) + .isEqualTo(statusBarChipIconView) + assertThat(statusBarChipIconView.contentDescription) + .isEqualTo(context.resources.getString(R.string.ongoing_phone_call_content_description)) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + fun onEntryUpdated_newNotifIcon_newIconUsed() { + val notification = createOngoingCallNotifEntry() + + val firstStatusBarChipIconView = + StatusBarIconView( + context, + /* slot= */ "OngoingCallControllerTest", + notification.sbn, + ) + notification.icons = + IconPack.buildPack( + /* statusBarIcon= */ mock(StatusBarIconView::class.java), + firstStatusBarChipIconView, + /* shelfIcon= */ mock(StatusBarIconView::class.java), + /* aodIcon= */ mock(StatusBarIconView::class.java), + /* source= */ null, + ) + notifCollectionListener.onEntryUpdated(notification) + + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)) + .isEqualTo(firstStatusBarChipIconView) + + // WHEN the notification is updated with a new icon + val secondStatusBarChipIconView = + StatusBarIconView( + context, + /* slot= */ "OngoingCallControllerTestTheSecond", + notification.sbn, + ) + notification.icons = + IconPack.buildPack( + /* statusBarIcon= */ mock(StatusBarIconView::class.java), + secondStatusBarChipIconView, + /* shelfIcon= */ mock(StatusBarIconView::class.java), + /* aodIcon= */ mock(StatusBarIconView::class.java), + /* source= */ null, + ) + notifCollectionListener.onEntryUpdated(notification) + + // THEN the new icon is used + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)) + .isEqualTo(secondStatusBarChipIconView) + } + + @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + fun chipViewReinflated_iconViewMovedToNewChip() { + val notification = createOngoingCallNotifEntry() + val statusBarChipIconView = + StatusBarIconView( + context, + /* slot= */ "OngoingCallControllerTest", + notification.sbn, + ) + notification.icons = + IconPack.buildPack( + /* statusBarIcon= */ mock(StatusBarIconView::class.java), + statusBarChipIconView, + /* shelfIcon= */ mock(StatusBarIconView::class.java), + /* aodIcon= */ mock(StatusBarIconView::class.java), + /* source= */ null, + ) + + notifCollectionListener.onEntryUpdated(notification) + + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)) + .isEqualTo(statusBarChipIconView) + + // WHEN we get a new chip view + lateinit var newChipView: View + TestableLooper.get(this).runWithLooper { + newChipView = + LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) + } + controller.setChipView(newChipView) + + // THEN the icon is detached from the old view and attached to the new one + assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_icon)).isNull() + assertThat(newChipView.findViewById<View>(R.id.ongoing_activity_chip_icon)) + .isEqualTo(statusBarChipIconView) + } + /** Regression test for b/194731244. */ @Test fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 8df37ceb2126..666bdd6a881a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -134,7 +134,6 @@ public class ToastUITest extends SysuiTestCase { mNotificationManager, mAccessibilityManager, new ToastFactory( - mLayoutInflater, mPluginManager, mDumpManager), mToastLogger); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt new file mode 100644 index 000000000000..1f04a44f172b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/activatable/ActivatableExt.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.activatable + +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.TestScope + +/** Activates [activatable] for the duration of the test. */ +fun Activatable.activateIn(testScope: TestScope) { + testScope.backgroundScope.launch { activate() } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt index a7a18a06aa8b..ef297d203f37 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt @@ -20,7 +20,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope -val Kosmos.fakeCommunalSceneRepository by Fixture { +var Kosmos.fakeCommunalSceneRepository by Fixture { FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt index fe156e2037cf..957f092c188a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt @@ -21,6 +21,8 @@ import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver +import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver import kotlinx.coroutines.ExperimentalCoroutinesApi @ExperimentalCoroutinesApi @@ -33,5 +35,7 @@ val Kosmos.keyguardDismissActionInteractor by applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, deviceEntryInteractor = deviceEntryInteractor, + quickSettingsSceneFamilyResolver = quickSettingsSceneFamilyResolver, + notifShadeSceneFamilyResolver = notifShadeSceneFamilyResolver, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 06668201a925..8614fc905934 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -60,6 +60,7 @@ import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shadeController import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel +import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.scrimController @@ -98,6 +99,7 @@ class KosmosJavaAdapter() { val communalRepository by lazy { kosmos.fakeCommunalSceneRepository } val communalTransitionViewModel by lazy { kosmos.communalTransitionViewModel } val headsUpNotificationInteractor by lazy { kosmos.headsUpNotificationInteractor } + val seenNotificationsInteractor by lazy { kosmos.seenNotificationsInteractor } val keyguardRepository by lazy { kosmos.fakeKeyguardRepository } val keyguardBouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository } val keyguardInteractor by lazy { kosmos.keyguardInteractor } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt index c56c56cddddf..299b22ef963f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModel @@ -27,6 +26,5 @@ val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by shadeInteractor = shadeInteractor, overlayShadeViewModel = overlayShadeViewModel, quickSettingsContainerViewModel = quickSettingsContainerViewModel, - applicationScope = applicationCoroutineScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt index fff3b14c95ec..dd931410b003 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt @@ -2,7 +2,6 @@ package com.android.systemui.scene import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.testScope import com.android.systemui.scene.shared.model.FakeScene import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes @@ -18,16 +17,7 @@ var Kosmos.sceneKeys by Fixture { ) } -val Kosmos.fakeScenes by Fixture { - sceneKeys - .map { key -> - FakeScene( - scope = testScope.backgroundScope, - key = key, - ) - } - .toSet() -} +val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet() } val Kosmos.scenes by Fixture { fakeScenes } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt index eeaa9db16730..64e3526603f9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt @@ -19,16 +19,12 @@ package com.android.systemui.scene.shared.model import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.flow.stateIn class FakeScene( - val scope: CoroutineScope, override val key: SceneKey, ) : Scene { var isDestinationScenesBeingCollected = false @@ -40,11 +36,6 @@ class FakeScene( .receiveAsFlow() .onStart { isDestinationScenesBeingCollected = true } .onCompletion { isDestinationScenesBeingCollected = false } - .stateIn( - scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = emptyMap(), - ) suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) { destinationScenesChannel.send(value) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt index 989c3a5d6d05..2c5a0f4d31bc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelKosmos.kt @@ -17,7 +17,6 @@ package com.android.systemui.shade.ui.viewmodel import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor import com.android.systemui.qs.footerActionsController import com.android.systemui.qs.footerActionsViewModelFactory @@ -30,7 +29,6 @@ import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor val Kosmos.shadeSceneViewModel: ShadeSceneViewModel by Kosmos.Fixture { ShadeSceneViewModel( - applicationScope = applicationCoroutineScope, shadeHeaderViewModel = shadeHeaderViewModel, qsSceneAdapter = qsSceneAdapter, brightnessMirrorViewModel = brightnessMirrorViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt index 77d97bb7cbe9..933ebf014fa1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorKosmos.kt @@ -18,23 +18,19 @@ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor -import com.android.systemui.util.settings.fakeSettings var Kosmos.lockScreenMinimalismCoordinator by Kosmos.Fixture { LockScreenMinimalismCoordinator( - bgDispatcher = testDispatcher, dumpManager = dumpManager, headsUpInteractor = headsUpNotificationInteractor, logger = lockScreenMinimalismCoordinatorLogger, scope = testScope.backgroundScope, - secureSettings = fakeSettings, seenNotificationsInteractor = seenNotificationsInteractor, statusBarStateController = statusBarStateController, shadeInteractor = shadeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt index c1e0419e5609..b19e221d099c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorKosmos.kt @@ -16,12 +16,32 @@ package com.android.systemui.statusbar.notification.domain.interactor +import android.os.UserHandle +import android.provider.Settings import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.util.settings.fakeSettings val Kosmos.seenNotificationsInteractor by Fixture { SeenNotificationsInteractor( + bgDispatcher = testDispatcher, notificationListRepository = activeNotificationListRepository, + secureSettings = fakeSettings, ) } + +var Kosmos.lockScreenShowOnlyUnseenNotificationsSetting: Boolean + get() = + fakeSettings.getIntForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + UserHandle.USER_CURRENT, + ) == 1 + set(value) { + fakeSettings.putIntForUser( + Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, + if (value) 1 else 2, + UserHandle.USER_CURRENT, + ) + } diff --git a/services/core/Android.bp b/services/core/Android.bp index 9d4310c21cf9..363c1d8c5f04 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -148,7 +148,7 @@ java_library_static { "android.hardware.common-V2-java", "android.hardware.light-V2.0-java", "android.hardware.gnss-V2-java", - "android.hardware.vibrator-V2-java", + "android.hardware.vibrator-V3-java", "app-compat-annotations", "framework-tethering.stubs.module_lib", "keepanno-annotations", diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 47b65eb885ab..1f88657e5dbc 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -353,8 +353,8 @@ public final class AppExitInfoTracker { } /** Called when there is a low memory kill */ - void scheduleNoteLmkdProcKilled(final int pid, final int uid) { - mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid) + void scheduleNoteLmkdProcKilled(final int pid, final int uid, final int rssKb) { + mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid, Long.valueOf(rssKb)) .sendToTarget(); } @@ -401,9 +401,9 @@ public final class AppExitInfoTracker { if (lmkd != null) { updateExistingExitInfoRecordLocked(info, null, - ApplicationExitInfo.REASON_LOW_MEMORY); + ApplicationExitInfo.REASON_LOW_MEMORY, (Long) lmkd.second); } else if (zygote != null) { - updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null); + updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null, null); } else { scheduleLogToStatsdLocked(info, false); } @@ -486,7 +486,7 @@ public final class AppExitInfoTracker { */ @GuardedBy("mLock") private void updateExistingExitInfoRecordLocked(ApplicationExitInfo info, - Integer status, Integer reason) { + Integer status, Integer reason, Long rssKb) { if (info == null || !isFresh(info.getTimestamp())) { // if the record is way outdated, don't update it then (because of potential pid reuse) return; @@ -513,6 +513,9 @@ public final class AppExitInfoTracker { immediateLog = true; } } + if (rssKb != null) { + info.setRss(rssKb.longValue()); + } scheduleLogToStatsdLocked(info, immediateLog); } @@ -523,7 +526,7 @@ public final class AppExitInfoTracker { */ @GuardedBy("mLock") private boolean updateExitInfoIfNecessaryLocked( - int pid, int uid, Integer status, Integer reason) { + int pid, int uid, Integer status, Integer reason, Long rssKb) { Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); if (k != null) { uid = k; @@ -552,7 +555,7 @@ public final class AppExitInfoTracker { // always be the first one we se as `getExitInfosLocked()` returns them sorted // by most-recent-first. isModified[0] = true; - updateExistingExitInfoRecordLocked(info, status, reason); + updateExistingExitInfoRecordLocked(info, status, reason, rssKb); return FOREACH_ACTION_STOP_ITERATION; } return FOREACH_ACTION_NONE; @@ -1668,11 +1671,11 @@ public final class AppExitInfoTracker { switch (msg.what) { case MSG_LMKD_PROC_KILLED: mAppExitInfoSourceLmkd.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */, - null /* status */); + null /* status */, (Long) msg.obj /* rss_kb */); break; case MSG_CHILD_PROC_DIED: mAppExitInfoSourceZygote.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */, - (Integer) msg.obj /* status */); + (Integer) msg.obj /* status */, null /* rss_kb */); break; case MSG_PROC_DIED: { ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; @@ -1833,7 +1836,7 @@ public final class AppExitInfoTracker { } } - void onProcDied(final int pid, final int uid, final Integer status) { + void onProcDied(final int pid, final int uid, final Integer status, final Long rssKb) { if (DEBUG_PROCESSES) { Slog.i(TAG, mTag + ": proc died: pid=" + pid + " uid=" + uid + ", status=" + status); @@ -1846,8 +1849,12 @@ public final class AppExitInfoTracker { // Unlikely but possible: the record has been created // Let's update it if we could find a ApplicationExitInfo record synchronized (mLock) { - if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) { - addLocked(pid, uid, status); + if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason, rssKb)) { + if (rssKb != null) { + addLocked(pid, uid, rssKb); // lmkd + } else { + addLocked(pid, uid, status); // zygote + } } // Notify any interesed party regarding the lmkd kills diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 779aabecbb99..726e8275ae2d 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -951,12 +951,14 @@ public final class ProcessList { try { switch (inputData.readInt()) { case LMK_PROCKILL: - if (receivedLen != 12) { + if (receivedLen != 16) { return false; } final int pid = inputData.readInt(); final int uid = inputData.readInt(); - mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid); + final int rssKb = inputData.readInt(); + mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid, + rssKb); return true; case LMK_KILL_OCCURRED: if (receivedLen diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 00b7b8ee3ebb..1183768a272b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -507,11 +507,31 @@ public class AudioService extends IAudioService.Stub return streamState != null ? streamState.getIndex(device) : -1; } + /** + * Returns the {@link VolumeStreamState} corresponding to the passed stream type. This can be + * {@code null} since not all possible stream types have a valid {@link VolumeStreamState} (e.g. + * {@link AudioSystem#STREAM_BLUETOOTH_SCO}) is deprecated and will return a {@code null} stream + * state). + * + * @param stream the stream type for querying the stream state + * + * @return the {@link VolumeStreamState} corresponding to the passed stream type or {@code null} + */ @Nullable /*package*/ VolumeStreamState getVssForStream(int stream) { return mStreamStates.get(stream); } + /** + * Returns the {@link VolumeStreamState} corresponding to the passed stream type. In case + * there is no associated stream state for the given stream type we return the default stream + * state for {@link AudioSystem#STREAM_MUSIC} (or throw an {@link IllegalArgumentException} in + * the ramp up phase of the replaceStreamBtSco flag to ensure that this case will never happen). + * + * @param stream the stream type for querying the stream state + * + * @return the {@link VolumeStreamState} corresponding to the passed stream type + */ @NonNull /*package*/ VolumeStreamState getVssForStreamOrDefault(int stream) { VolumeStreamState streamState = mStreamStates.get(stream); @@ -3672,6 +3692,11 @@ public class AudioService extends IAudioService.Stub ensureValidStreamType(streamType); final int resolvedStream = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1); + if (resolvedStream == -1) { + Log.e(TAG, "adjustSuggestedStreamVolume: no stream vol alias for stream type " + + streamType); + return; + } // Play sounds on STREAM_RING only. if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && @@ -3789,6 +3814,10 @@ public class AudioService extends IAudioService.Stub // including with regard to silent mode control (e.g the use of STREAM_RING below and in // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) int streamTypeAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1); + if (streamTypeAlias == -1) { + Log.e(TAG, + "adjustStreamVolume: no stream vol alias for stream type " + streamType); + } VolumeStreamState streamState = getVssForStreamOrDefault(streamTypeAlias); @@ -4057,8 +4086,8 @@ public class AudioService extends IAudioService.Stub synchronized (mSettingsLock) { synchronized (VolumeStreamState.class) { List<Integer> streamsToMute = new ArrayList<>(); - for (int stream = 0; stream < mStreamStates.size(); stream++) { - final VolumeStreamState vss = mStreamStates.valueAt(stream); + for (int streamIdx = 0; streamIdx < mStreamStates.size(); streamIdx++) { + final VolumeStreamState vss = mStreamStates.valueAt(streamIdx); if (vss != null && streamAlias == sStreamVolumeAlias.get(vss.getStreamType()) && vss.isMutable()) { if (!(mCameraSoundForced && (vss.getStreamType() @@ -4199,6 +4228,10 @@ public class AudioService extends IAudioService.Stub /*package*/ void onSetStreamVolume(int streamType, int index, int flags, int device, String caller, boolean hasModifyAudioSettings, boolean canChangeMute) { final int stream = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/-1); + if (stream == -1) { + Log.e(TAG, "onSetStreamVolume: no stream vol alias for stream type " + stream); + return; + } // setting volume on ui sounds stream type also controls silent mode if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (stream == getUiSoundsStreamType())) { @@ -4848,6 +4881,10 @@ public class AudioService extends IAudioService.Stub ensureValidStreamType(streamType); int streamTypeAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound*/-1); + if (streamTypeAlias == -1) { + Log.e(TAG, "setStreamVolume: no stream vol alias for stream type " + streamType); + return; + } final VolumeStreamState streamState = getVssForStreamOrDefault(streamTypeAlias); if (!replaceStreamBtSco() && (streamType == AudioManager.STREAM_VOICE_CALL) @@ -5566,9 +5603,9 @@ public class AudioService extends IAudioService.Stub return new ArrayList<>(Arrays.stream(AudioManager.getPublicStreamTypes()) .boxed().toList()); } - ArrayList<Integer> res = new ArrayList(1); - for (int stream = 0; stream < sStreamVolumeAlias.size(); ++stream) { - final int streamAlias = sStreamVolumeAlias.valueAt(stream); + ArrayList<Integer> res = new ArrayList<>(1); + for (int streamIdx = 0; streamIdx < sStreamVolumeAlias.size(); ++streamIdx) { + final int streamAlias = sStreamVolumeAlias.valueAt(streamIdx); if (!res.contains(streamAlias)) { res.add(streamAlias); } @@ -6388,6 +6425,10 @@ public class AudioService extends IAudioService.Stub final int device = getDeviceForStream(streamType); final int streamAlias = sStreamVolumeAlias.get(streamType, /*valueIfKeyNotFound=*/ -1); + if (streamAlias == -1) { + Log.e(TAG, + "onUpdateAudioMode: no stream vol alias for stream type " + streamType); + } if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode: streamType=" + streamType @@ -7594,8 +7635,8 @@ public class AudioService extends IAudioService.Stub ? MIN_STREAM_VOLUME[AudioSystem.STREAM_ALARM] : Math.min(idx + 1, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); // update the VolumeStreamState for STREAM_ALARM and its aliases - for (int stream = 0; stream < sStreamVolumeAlias.size(); ++stream) { - final int streamAlias = sStreamVolumeAlias.valueAt(stream); + for (int streamIdx = 0; streamIdx < sStreamVolumeAlias.size(); ++streamIdx) { + final int streamAlias = sStreamVolumeAlias.valueAt(streamIdx); if (streamAlias == AudioSystem.STREAM_ALARM) { getVssForStreamOrDefault(streamAlias).updateNoPermMinIndex(safeIndex); } @@ -9434,7 +9475,7 @@ public class AudioService extends IAudioService.Stub // must be sync'd on mSettingsLock before VolumeStreamState.class @GuardedBy("VolumeStreamState.class") public void setAllIndexes(VolumeStreamState srcStream, String caller) { - if (mStreamType == srcStream.mStreamType) { + if (srcStream == null || mStreamType == srcStream.mStreamType) { return; } int srcStreamType = srcStream.getStreamType(); diff --git a/services/core/java/com/android/server/crashrecovery/TEST_MAPPING b/services/core/java/com/android/server/crashrecovery/TEST_MAPPING index 615db345635c..537fb32523b5 100644 --- a/services/core/java/com/android/server/crashrecovery/TEST_MAPPING +++ b/services/core/java/com/android/server/crashrecovery/TEST_MAPPING @@ -1,4 +1,9 @@ { + "presubmit": [ + { + "name": "CrashRecoveryModuleTests" + } + ], "postsubmit": [ { "name": "FrameworksMockingServicesTests", @@ -7,9 +12,6 @@ "include-filter": "com.android.server.RescuePartyTest" } ] - }, - { - "name": "CrashRecoveryModuleTests" } ] }
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1177be212222..8b21d98045dd 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1755,6 +1755,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); mTempBrightnessEvent.setPhysicalDisplayName(mPhysicalDisplayName); mTempBrightnessEvent.setDisplayState(state); + mTempBrightnessEvent.setDisplayStateReason(stateAndReason.second); mTempBrightnessEvent.setDisplayPolicy(mPowerRequest.policy); mTempBrightnessEvent.setReason(mBrightnessReason); mTempBrightnessEvent.setHbmMax(hbmMax); diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java index 5cc603c5018c..ad57ebfb0600 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java @@ -52,6 +52,8 @@ public final class BrightnessEvent { private String mPhysicalDisplayId; private String mPhysicalDisplayName; private int mDisplayState; + @Display.StateReason + private int mDisplayStateReason; private int mDisplayPolicy; private long mTime; private float mLux; @@ -96,6 +98,7 @@ public final class BrightnessEvent { mPhysicalDisplayId = that.getPhysicalDisplayId(); mPhysicalDisplayName = that.getPhysicalDisplayName(); mDisplayState = that.mDisplayState; + mDisplayStateReason = that.mDisplayStateReason; mDisplayPolicy = that.mDisplayPolicy; mTime = that.getTime(); // Lux values @@ -133,6 +136,7 @@ public final class BrightnessEvent { mPhysicalDisplayId = ""; mPhysicalDisplayName = ""; mDisplayState = Display.STATE_UNKNOWN; + mDisplayStateReason = Display.STATE_REASON_UNKNOWN; mDisplayPolicy = POLICY_OFF; // Lux values mLux = INVALID_LUX; @@ -176,6 +180,7 @@ public final class BrightnessEvent { && mPhysicalDisplayId.equals(that.mPhysicalDisplayId) && mPhysicalDisplayName.equals(that.mPhysicalDisplayName) && mDisplayState == that.mDisplayState + && mDisplayStateReason == that.mDisplayStateReason && mDisplayPolicy == that.mDisplayPolicy && Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux) && Float.floatToRawIntBits(mPreThresholdLux) @@ -221,6 +226,7 @@ public final class BrightnessEvent { + ", reason=" + mReason.toString(mAdjustmentFlags) + ", strat=" + mDisplayBrightnessStrategyName + ", state=" + Display.stateToString(mDisplayState) + + ", stateReason=" + Display.stateReasonToString(mDisplayStateReason) + ", policy=" + policyToString(mDisplayPolicy) + ", flags=" + flagsToString() // Autobrightness @@ -293,6 +299,10 @@ public final class BrightnessEvent { mDisplayState = state; } + public void setDisplayStateReason(@Display.StateReason int reason) { + mDisplayStateReason = reason; + } + public void setDisplayPolicy(int policy) { mDisplayPolicy = policy; } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 886857c1b880..d43e783cad41 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -544,11 +544,10 @@ public final class DreamManagerService extends SystemService { private void startDozingInternal(IBinder token, int screenState, @Display.StateReason int reason, int screenBrightness) { - if (DEBUG) { - Slog.d(TAG, "Dream requested to start dozing: " + token - + ", screenState=" + screenState - + ", screenBrightness=" + screenBrightness); - } + Slog.d(TAG, "Dream requested to start dozing: " + token + + ", screenState=" + Display.stateToString(screenState) + + ", reason=" + Display.stateReasonToString(reason) + + ", screenBrightness=" + screenBrightness); synchronized (mLock) { if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.canDoze) { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index bb2efa166800..36a9c80717f2 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1355,8 +1355,7 @@ public class InputManagerService extends IInputManager.Stub int patternRepeatIndex = -1; int amplitudeCount = -1; - if (effect instanceof VibrationEffect.Composed) { - VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + if (effect instanceof VibrationEffect.Composed composed) { int segmentCount = composed.getSegments().size(); pattern = new long[segmentCount]; amplitudes = new int[segmentCount]; @@ -1381,6 +1380,8 @@ public class InputManagerService extends IInputManager.Stub } pattern[amplitudeCount++] = segment.getDuration(); } + } else { + Slog.w(TAG, "Input devices don't support effect " + effect); } if (amplitudeCount < 0) { diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 49934126ab8c..1d1a178ff20b 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -22,6 +22,8 @@ import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECT import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT; +import static com.android.hardware.input.Flags.keyboardLayoutManagerMultiUserImeSetup; + import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.NonNull; @@ -1066,9 +1068,15 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { for (InputMethodInfo imeInfo : inputMethodManagerInternal.getEnabledInputMethodListAsUser( userId)) { - for (InputMethodSubtype imeSubtype : - inputMethodManager.getEnabledInputMethodSubtypeList( - imeInfo, true /* allowsImplicitlyEnabledSubtypes */)) { + final List<InputMethodSubtype> imeSubtypes; + if (keyboardLayoutManagerMultiUserImeSetup()) { + imeSubtypes = inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser( + imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */, userId); + } else { + imeSubtypes = inputMethodManager.getEnabledInputMethodSubtypeList(imeInfo, + true /* allowsImplicitlyEnabledSubtypes */); + } + for (InputMethodSubtype imeSubtype : imeSubtypes) { if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) { continue; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index eed34b86f744..4716e6c24154 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -170,6 +170,7 @@ import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; import com.android.internal.os.TransferPipe; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.AccessibilityManagerInternal; @@ -270,7 +271,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; - private static final int MSG_SYSTEM_UNLOCK_USER = 5000; private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010; private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000; @@ -569,13 +569,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void onSecureSettingsChangedLocked(@NonNull String key, @UserIdInt int userId) { - if (!mConcurrentMultiUserModeEnabled && userId != mCurrentUserId) { - return; - } switch (key) { case Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD: { if (!Flags.imeSwitcherRevamp()) { - mMenuController.updateKeyboardFromSettingsLocked(); + if (userId == mCurrentUserId) { + mMenuController.updateKeyboardFromSettingsLocked(); + } } break; } @@ -678,12 +677,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(userId, settings); - if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) { - postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId); - // If the locale is changed, needs to reset the default ime - resetDefaultImeLocked(mContext, userId); - updateFromSettingsLocked(true, userId); - } + postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId); + // If the locale is changed, needs to reset the default ime + resetDefaultImeLocked(mContext, userId); + updateFromSettingsLocked(true, userId); } } } @@ -762,7 +759,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. .getMethodMap(); synchronized (ImfLock.class) { - final boolean isCurrentUser = (userId == mCurrentUserId); final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId); final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); @@ -785,14 +781,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. int change = isPackageDisappearing(imi.getPackageName()); if (change == PACKAGE_PERMANENT_CHANGE) { Slog.i(TAG, "Input method uninstalled, disabling: " + imi.getComponent()); - if (isCurrentUser) { - setInputMethodEnabledLocked(imi.getId(), false, userId); - } else { - settings.buildAndPutEnabledInputMethodsStrRemovingId( - new StringBuilder(), - settings.getEnabledInputMethodsAndSubtypeList(), - imi.getId()); - } + setInputMethodEnabledLocked(imi.getId(), false, userId); } else if (change == PACKAGE_UPDATING) { Slog.i(TAG, "Input method reinstalling, clearing additional subtypes: " + imi.getComponent()); @@ -821,9 +810,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputMethodSettings newSettings = InputMethodSettings.create(newMethodMap, userId); InputMethodSettingsRepository.put(userId, newSettings); - if (!isCurrentUser) { - return; - } postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId); boolean changed = false; @@ -1033,10 +1019,24 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void onUserUnlocking(@NonNull TargetUser user) { - // Called on ActivityManager thread. - SecureSettingsWrapper.onUserUnlocking(user.getUserIdentifier()); - mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER, user.getUserIdentifier(), 0) - .sendToTarget(); + // Called on ActivityManager thread. Do not block the calling thread. + final int userId = user.getUserIdentifier(); + SecureSettingsWrapper.onUserUnlocking(userId); + mService.mIoHandler.post(() -> { + final var settings = queryInputMethodServicesInternal(mService.mContext, userId, + AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO); + InputMethodSettingsRepository.put(userId, settings); + synchronized (ImfLock.class) { + if (!mService.mSystemReady) { + return; + } + // We need to rebuild IMEs. + mService.postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, + userId); + mService.updateInputMethodsFromSettingsLocked(true /* enabledChanged */, + userId); + } + }); } @Override @@ -1087,23 +1087,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - void onUnlockUser(@UserIdInt int userId) { - synchronized (ImfLock.class) { - if (DEBUG) { - Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + mCurrentUserId); - } - if (!mSystemReady) { - return; - } - final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext, - userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO); - InputMethodSettingsRepository.put(userId, newSettings); - // We need to rebuild IMEs. - postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId); - updateInputMethodsFromSettingsLocked(true /* enabledChanged */, userId); - } - } - @GuardedBy("ImfLock.class") void scheduleSwitchUserTaskLocked(@UserIdInt int userId, @Nullable IInputMethodClientInvoker clientToBeReset) { @@ -3975,6 +3958,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public void showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode) { + if (mConcurrentMultiUserModeEnabled) { + Slog.w(TAG, "showInputMethodPickerFromClient is not enabled on automotive"); + return; + } final int callingUserId = UserHandle.getCallingUserId(); synchronized (ImfLock.class) { if (!canShowInputMethodPickerLocked(client)) { @@ -5144,11 +5131,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. sendOnNavButtonFlagsChangedToAllImesLocked(); } return true; - case MSG_SYSTEM_UNLOCK_USER: { - final int userId = msg.arg1; - onUnlockUser(userId); - return true; - } case MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED: { final int userId = msg.arg1; final List<InputMethodInfo> imes = (List<InputMethodInfo>) msg.obj; @@ -5700,24 +5682,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean switchToInputMethodLocked(@NonNull String imeId, int subtypeId, @UserIdInt int userId) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) { - if (!settings.getMethodMap().containsKey(imeId) - || !settings.getEnabledInputMethodList() - .contains(settings.getMethodMap().get(imeId))) { - return false; // IME is not found or not enabled. - } - setInputMethodLocked(imeId, subtypeId, userId); - return true; - } - if (!settings.getMethodMap().containsKey(imeId) - || !settings.getEnabledInputMethodList().contains( - settings.getMethodMap().get(imeId))) { + final var settings = InputMethodSettingsRepository.get(userId); + final var enabledImes = settings.getEnabledInputMethodList(); + if (!CollectionUtils.any(enabledImes, imi -> imi.getId().equals(imeId))) { return false; // IME is not found or not enabled. } - settings.putSelectedInputMethod(imeId); - // For non-current user, only reset subtypeId (instead of setting the given one). - settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + setInputMethodLocked(imeId, subtypeId, userId); return true; } @@ -5884,22 +5854,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!settings.getMethodMap().containsKey(imeId)) { return false; // IME is not found. } - if (userId == mCurrentUserId) { - setInputMethodEnabledLocked(imeId, enabled, userId); - return true; - } - if (enabled) { - final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); - final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( - enabledImeIdsStr, imeId); - if (!TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) { - settings.putEnabledInputMethodsStr(newEnabledImeIdsStr); - } - } else { - settings.buildAndPutEnabledInputMethodsStrRemovingId( - new StringBuilder(), - settings.getEnabledInputMethodsAndSubtypeList(), imeId); - } + setInputMethodEnabledLocked(imeId, enabled, userId); return true; } } @@ -6707,36 +6662,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private boolean handleShellCommandEnableDisableInputMethodInternalLocked( @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out, PrintWriter error) { - boolean failedToEnableUnknownIme = false; - boolean previouslyEnabled = false; final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - if (userId == mCurrentUserId) { - if (enabled && !settings.getMethodMap().containsKey(imeId)) { - failedToEnableUnknownIme = true; - } else { - previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled, userId); - } - } else { - if (enabled) { - if (!settings.getMethodMap().containsKey(imeId)) { - failedToEnableUnknownIme = true; - } else { - final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); - final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( - enabledImeIdsStr, imeId); - previouslyEnabled = TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr); - if (!previouslyEnabled) { - settings.putEnabledInputMethodsStr(newEnabledImeIdsStr); - } - } - } else { - previouslyEnabled = - settings.buildAndPutEnabledInputMethodsStrRemovingId( - new StringBuilder(), - settings.getEnabledInputMethodsAndSubtypeList(), imeId); - } - } - if (failedToEnableUnknownIme) { + if (enabled && !settings.getMethodMap().containsKey(imeId)) { error.print("Unknown input method "); error.print(imeId); error.println(" cannot be enabled for user #" + userId); @@ -6745,6 +6672,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. + " failed due to its unrecognized IME ID."); return false; } + + final boolean previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled, userId); out.print("Input method "); out.print(imeId); out.print(": "); @@ -6825,67 +6754,47 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final String nextIme; final List<InputMethodInfo> nextEnabledImes; final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - if (userId == mCurrentUserId) { - final var userData = getUserData(userId); - if (Flags.refactorInsetsController()) { - if (userData.mImeBindingState != null - && userData.mImeBindingState.mFocusedWindowClient != null - && userData.mImeBindingState.mFocusedWindowClient.mClient - != null) { - userData.mImeBindingState.mFocusedWindowClient.mClient - .setImeVisibility(false, - null /* TODO(b329229469) initialize statsToken here? */); - } else { - // TODO(b329229469): ImeTracker? - } + final var userData = getUserData(userId); + if (Flags.refactorInsetsController()) { + if (userData.mImeBindingState != null + && userData.mImeBindingState.mFocusedWindowClient != null + && userData.mImeBindingState.mFocusedWindowClient.mClient + != null) { + userData.mImeBindingState.mFocusedWindowClient.mClient + .setImeVisibility(false, + null /* TODO(b329229469) initialize statsToken here? */); } else { - hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, - 0 /* flags */, - SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId); - } - final var bindingController = userData.mBindingController; - bindingController.unbindCurrentMethod(); - - // Enable default IMEs, disable others - var toDisable = settings.getEnabledInputMethodList(); - var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes( - mContext, settings.getMethodList()); - toDisable.removeAll(defaultEnabled); - for (InputMethodInfo info : toDisable) { - setInputMethodEnabledLocked(info.getId(), false, userId); + // TODO(b329229469): ImeTracker? } - for (InputMethodInfo info : defaultEnabled) { - setInputMethodEnabledLocked(info.getId(), true, userId); - } - // Choose new default IME, reset to none if no IME available. - if (!chooseNewDefaultIMELocked(userId)) { - resetSelectedInputMethodAndSubtypeLocked(null, userId); - } - updateInputMethodsFromSettingsLocked(true /* enabledMayChange */, userId); - InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( - getPackageManagerForUser(mContext, settings.getUserId()), - settings.getEnabledInputMethodList()); - nextIme = settings.getSelectedInputMethod(); - nextEnabledImes = settings.getEnabledInputMethodList(); } else { - nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext, - settings.getMethodList()); - nextIme = InputMethodInfoUtils.getMostApplicableDefaultIME( - nextEnabledImes).getId(); - - // Reset enabled IMEs. - final String[] nextEnabledImeIds = new String[nextEnabledImes.size()]; - for (int i = 0; i < nextEnabledImeIds.length; ++i) { - nextEnabledImeIds[i] = nextEnabledImes.get(i).getId(); - } - settings.putEnabledInputMethodsStr(InputMethodUtils.concatEnabledImeIds( - "", nextEnabledImeIds)); + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId); + } + final var bindingController = userData.mBindingController; + bindingController.unbindCurrentMethod(); - // Reset selected IME. - settings.putSelectedInputMethod(nextIme); - settings.putSelectedDefaultDeviceInputMethod(null); - settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + // Enable default IMEs, disable others + var toDisable = settings.getEnabledInputMethodList(); + var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes( + mContext, settings.getMethodList()); + toDisable.removeAll(defaultEnabled); + for (InputMethodInfo info : toDisable) { + setInputMethodEnabledLocked(info.getId(), false, userId); + } + for (InputMethodInfo info : defaultEnabled) { + setInputMethodEnabledLocked(info.getId(), true, userId); + } + // Choose new default IME, reset to none if no IME available. + if (!chooseNewDefaultIMELocked(userId)) { + resetSelectedInputMethodAndSubtypeLocked(null, userId); } + updateInputMethodsFromSettingsLocked(true /* enabledMayChange */, userId); + InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( + getPackageManagerForUser(mContext, settings.getUserId()), + settings.getEnabledInputMethodList()); + nextIme = settings.getSelectedInputMethod(); + nextEnabledImes = settings.getEnabledInputMethodList(); out.println("Reset current and enabled IMEs for user #" + userId); out.println(" Selected: " + nextIme); nextEnabledImes.forEach(ime -> out.println(" Enabled: " + ime.getId())); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 9acf0301ce42..1070f2f8faf1 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -328,6 +328,12 @@ class MediaRouter2ServiceImpl { } } + @RequiresPermission( + anyOf = { + Manifest.permission.MEDIA_ROUTING_CONTROL, + Manifest.permission.MEDIA_CONTENT_CONTROL + }, + conditional = true) public void updateScanningState( @NonNull IMediaRouter2 router, @ScanningState int scanningState) { Objects.requireNonNull(router, "router must not be null"); @@ -1216,6 +1222,12 @@ class MediaRouter2ServiceImpl { disposeUserIfNeededLocked(userRecord); // since router removed from user } + @RequiresPermission( + anyOf = { + Manifest.permission.MEDIA_ROUTING_CONTROL, + Manifest.permission.MEDIA_CONTENT_CONTROL + }, + conditional = true) @GuardedBy("mLock") private void updateScanningStateLocked( @NonNull IMediaRouter2 router, @ScanningState int scanningState) { @@ -1226,7 +1238,11 @@ class MediaRouter2ServiceImpl { return; } + boolean enableScanViaMediaContentControl = + Flags.enableFullScanWithMediaContentControl() + && routerRecord.mHasMediaContentControlPermission; if (scanningState == SCANNING_STATE_SCANNING_FULL + && !enableScanViaMediaContentControl && !routerRecord.mHasMediaRoutingControl) { throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL"); } @@ -1679,7 +1695,11 @@ class MediaRouter2ServiceImpl { return; } + boolean enableScanViaMediaContentControl = + Flags.enableFullScanWithMediaContentControl() + && managerRecord.mHasMediaContentControl; if (!managerRecord.mHasMediaRoutingControl + && !enableScanViaMediaContentControl && scanningState == SCANNING_STATE_SCANNING_FULL) { throw new SecurityException("Screen off scan requires MEDIA_ROUTING_CONTROL"); } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 1188a0764051..363b8e4228b0 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -443,6 +443,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override + @RequiresPermission( + anyOf = { + Manifest.permission.MEDIA_ROUTING_CONTROL, + Manifest.permission.MEDIA_CONTENT_CONTROL + }, + conditional = true) public void updateScanningStateWithRouter2( IMediaRouter2 router, @ScanningState int scanningState) { mService2.updateScanningState(router, scanningState); diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java index 613ebd384b2a..46207c1860c0 100644 --- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java +++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java @@ -579,11 +579,11 @@ public final class UserVisibilityMediator implements Dumpable { + " to user %d on start", userId, displayId, userAssignedToDisplay); return false; } - // Then if was assigned extra - userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(userId, USER_NULL); + // Then if the display was assigned before + userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL); if (userAssignedToDisplay != USER_NULL) { Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user %d was already " - + "assigned that extra display", userId, displayId, userAssignedToDisplay); + + "assigned to extra display", userId, displayId, userAssignedToDisplay); return false; } if (DBG) { diff --git a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java index ab22e3e3f94c..1003a8152b92 100644 --- a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java +++ b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java @@ -126,7 +126,7 @@ public class GnssPowerCalculator extends PowerCalculator { long totalTime = 0; double totalPower = 0; for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) { - long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType); + long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType) / 1000; totalTime += timePerLevel; totalPower += mAveragePowerPerSignalQuality[i] * timePerLevel; } diff --git a/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java new file mode 100644 index 000000000000..b2631597dd76 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/AbstractComposedVibratorStep.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 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.server.vibrator; + +import android.os.SystemClock; +import android.os.VibrationEffect; + +import java.util.List; + +/** + * Represent a step on a single vibrator that plays one or more segments from a + * {@link VibrationEffect.Composed} effect. + */ +abstract class AbstractComposedVibratorStep extends AbstractVibratorStep { + public final VibrationEffect.Composed effect; + public final int segmentIndex; + + /** + * @param conductor The {@link VibrationStepConductor} for these steps. + * @param startTime The time to schedule this step in the conductor. + * @param controller The vibrator that is playing the effect. + * @param effect The effect being played in this step. + * @param index The index of the next segment to be played by this step + * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any + * previous vibration and turn off. This is used to allow this step to + * be triggered when the completion callback is received, and can + * be used to play effects back-to-back. + */ + AbstractComposedVibratorStep(VibrationStepConductor conductor, long startTime, + VibratorController controller, VibrationEffect.Composed effect, int index, + long pendingVibratorOffDeadline) { + super(conductor, startTime, controller, pendingVibratorOffDeadline); + this.effect = effect; + this.segmentIndex = index; + } + + /** + * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings + * calculated from {@link #getVibratorOnDuration()} based on the current + * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect. + */ + protected List<Step> nextSteps(int segmentsPlayed) { + // Schedule next steps to run right away. + long nextStartTime = SystemClock.uptimeMillis(); + if (mVibratorOnResult > 0) { + // Vibrator was turned on by this step, with mVibratorOnResult as the duration. + // Schedule next steps for right after the vibration finishes. + nextStartTime += mVibratorOnResult; + } + return nextSteps(nextStartTime, segmentsPlayed); + } + + /** + * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time, + * which might be calculated independently, and jumping all played segments from the effect. + * + * <p>This should be used when the vibrator on/off state is not responsible for the step + * execution timing, e.g. while playing the vibrator amplitudes. + */ + protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) { + int nextSegmentIndex = segmentIndex + segmentsPlayed; + int effectSize = effect.getSegments().size(); + int repeatIndex = effect.getRepeatIndex(); + if (nextSegmentIndex >= effectSize && repeatIndex >= 0) { + // Count the loops that were played. + int loopSize = effectSize - repeatIndex; + int loopSegmentsPlayed = nextSegmentIndex - repeatIndex; + getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize); + nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize); + } + Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect, + nextSegmentIndex, mPendingVibratorOffDeadline); + return List.of(nextStep); + } +} diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java index 90b6f95f8740..42203b113498 100644 --- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java @@ -16,21 +16,16 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.SystemClock; -import android.os.VibrationEffect; import android.util.Slog; import java.util.Arrays; import java.util.List; -/** - * Represent a step on a single vibrator that plays one or more segments from a - * {@link VibrationEffect.Composed} effect. - */ +/** Represent a step on a single vibrator that plays a command on {@link VibratorController}. */ abstract class AbstractVibratorStep extends Step { public final VibratorController controller; - public final VibrationEffect.Composed effect; - public final int segmentIndex; long mVibratorOnResult; long mPendingVibratorOffDeadline; @@ -41,20 +36,15 @@ abstract class AbstractVibratorStep extends Step { * @param startTime The time to schedule this step in the * {@link VibrationStepConductor}. * @param controller The vibrator that is playing the effect. - * @param effect The effect being played in this step. - * @param index The index of the next segment to be played by this step * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any * previous vibration and turn off. This is used to allow this step to * be triggered when the completion callback is received, and can * be used to play effects back-to-back. */ AbstractVibratorStep(VibrationStepConductor conductor, long startTime, - VibratorController controller, VibrationEffect.Composed effect, int index, - long pendingVibratorOffDeadline) { + VibratorController controller, long pendingVibratorOffDeadline) { super(conductor, startTime); this.controller = controller; - this.effect = effect; - this.segmentIndex = index; mPendingVibratorOffDeadline = pendingVibratorOffDeadline; } @@ -88,6 +78,7 @@ abstract class AbstractVibratorStep extends Step { return shouldAcceptCallback; } + @NonNull @Override public List<Step> cancel() { return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(), @@ -138,43 +129,4 @@ abstract class AbstractVibratorStep extends Step { controller.setAmplitude(amplitude); getVibration().stats.reportSetAmplitude(); } - - /** - * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings - * calculated from {@link #getVibratorOnDuration()} based on the current - * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect. - */ - protected List<Step> nextSteps(int segmentsPlayed) { - // Schedule next steps to run right away. - long nextStartTime = SystemClock.uptimeMillis(); - if (mVibratorOnResult > 0) { - // Vibrator was turned on by this step, with mVibratorOnResult as the duration. - // Schedule next steps for right after the vibration finishes. - nextStartTime += mVibratorOnResult; - } - return nextSteps(nextStartTime, segmentsPlayed); - } - - /** - * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time, - * which might be calculated independently, and jumping all played segments from the effect. - * - * <p>This should be used when the vibrator on/off state is not responsible for the step - * execution timing, e.g. while playing the vibrator amplitudes. - */ - protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) { - int nextSegmentIndex = segmentIndex + segmentsPlayed; - int effectSize = effect.getSegments().size(); - int repeatIndex = effect.getRepeatIndex(); - if (nextSegmentIndex >= effectSize && repeatIndex >= 0) { - // Count the loops that were played. - int loopSize = effectSize - repeatIndex; - int loopSegmentsPlayed = nextSegmentIndex - repeatIndex; - getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize); - nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize); - } - Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect, - nextSegmentIndex, mPendingVibratorOffDeadline); - return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep); - } } diff --git a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java index 48dd992008d2..7f9c349b6d10 100644 --- a/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/CompleteEffectVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.SystemClock; import android.os.Trace; import android.os.VibrationEffect; @@ -35,8 +36,7 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep { CompleteEffectVibratorStep(VibrationStepConductor conductor, long startTime, boolean cancelled, VibratorController controller, long pendingVibratorOffDeadline) { - super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, - pendingVibratorOffDeadline); + super(conductor, startTime, controller, pendingVibratorOffDeadline); mCancelled = cancelled; } @@ -47,6 +47,7 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep { return mCancelled; } + @NonNull @Override public List<Step> cancel() { if (mCancelled) { @@ -57,6 +58,7 @@ final class CompleteEffectVibratorStep extends AbstractVibratorStep { return super.cancel(); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteEffectVibratorStep"); diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java index 940bd08eee4b..e495af59a2f9 100644 --- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.Trace; import android.os.VibrationEffect; import android.os.vibrator.PrimitiveSegment; @@ -31,7 +32,7 @@ import java.util.List; * <p>This step will use the maximum supported number of consecutive segments of type * {@link PrimitiveSegment} starting at the current index. */ -final class ComposePrimitivesVibratorStep extends AbstractVibratorStep { +final class ComposePrimitivesVibratorStep extends AbstractComposedVibratorStep { /** * Default limit to the number of primitives in a composition, if none is defined by the HAL, * to prevent repeating effects from generating an infinite list. @@ -47,6 +48,7 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep { index, pendingVibratorOffDeadline); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep"); diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java index 5d572be69246..e8952fafaf77 100644 --- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.Trace; import android.os.VibrationEffect; import android.os.vibrator.RampSegment; @@ -31,7 +32,7 @@ import java.util.List; * <p>This step will use the maximum supported number of consecutive segments of type * {@link RampSegment}, starting at the current index. */ -final class ComposePwleVibratorStep extends AbstractVibratorStep { +final class ComposePwleVibratorStep extends AbstractComposedVibratorStep { /** * Default limit to the number of PWLE segments, if none is defined by the HAL, to prevent * repeating effects from generating an infinite list. @@ -47,6 +48,7 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep { index, pendingVibratorOffDeadline); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep"); diff --git a/services/core/java/com/android/server/vibrator/DeviceAdapter.java b/services/core/java/com/android/server/vibrator/DeviceAdapter.java index 98309cd00758..bd4fc07fe816 100644 --- a/services/core/java/com/android/server/vibrator/DeviceAdapter.java +++ b/services/core/java/com/android/server/vibrator/DeviceAdapter.java @@ -21,7 +21,6 @@ import android.os.CombinedVibration; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.VibrationEffectSegment; -import android.util.Slog; import android.util.SparseArray; import java.util.ArrayList; @@ -82,9 +81,8 @@ final class DeviceAdapter implements CombinedVibration.VibratorAdapter { @NonNull @Override public VibrationEffect adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect) { - if (!(effect instanceof VibrationEffect.Composed)) { + if (!(effect instanceof VibrationEffect.Composed composed)) { // Segments adapters can only apply to Composed effects. - Slog.wtf(TAG, "Error adapting unsupported vibration effect: " + effect); return effect; } @@ -95,7 +93,6 @@ final class DeviceAdapter implements CombinedVibration.VibratorAdapter { } VibratorInfo info = controller.getVibratorInfo(); - VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments()); int newRepeatIndex = composed.getRepeatIndex(); diff --git a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java index c9683d9f69ed..6456371a52fe 100644 --- a/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java +++ b/services/core/java/com/android/server/vibrator/FinishSequentialEffectStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.Trace; import android.util.Slog; @@ -43,6 +44,7 @@ final class FinishSequentialEffectStep extends Step { return true; } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishSequentialEffectStep"); @@ -61,6 +63,7 @@ final class FinishSequentialEffectStep extends Step { } } + @NonNull @Override public List<Step> cancel() { cancelImmediately(); diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java index 8094e7c5c58e..4b23216258af 100644 --- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.Trace; import android.os.VibrationEffect; import android.os.vibrator.PrebakedSegment; @@ -31,7 +32,7 @@ import java.util.List; * <p>This step automatically falls back by replacing the prebaked segment with * {@link VibrationSettings#getFallbackEffect(int)}, if available. */ -final class PerformPrebakedVibratorStep extends AbstractVibratorStep { +final class PerformPrebakedVibratorStep extends AbstractComposedVibratorStep { PerformPrebakedVibratorStep(VibrationStepConductor conductor, long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, @@ -42,6 +43,7 @@ final class PerformPrebakedVibratorStep extends AbstractVibratorStep { index, pendingVibratorOffDeadline); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformPrebakedVibratorStep"); diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java new file mode 100644 index 000000000000..8f36118543ed --- /dev/null +++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 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.server.vibrator; + +import android.annotation.NonNull; +import android.os.Trace; +import android.os.VibrationEffect; + +import java.util.List; + +/** + * Represents a step to turn the vibrator on with a vendor-specific vibration from a + * {@link VibrationEffect.VendorEffect} effect. + */ +final class PerformVendorEffectVibratorStep extends AbstractVibratorStep { + /** + * Timeout to ensure vendor vibrations are not unbounded if vibrator callbacks are lost. + */ + static final long VENDOR_EFFECT_MAX_DURATION_MS = 60_000; // 1 min + + public final VibrationEffect.VendorEffect effect; + + PerformVendorEffectVibratorStep(VibrationStepConductor conductor, long startTime, + VibratorController controller, VibrationEffect.VendorEffect effect, + long pendingVibratorOffDeadline) { + // This step should wait for the last vibration to finish (with the timeout) and for the + // intended step start time (to respect the effect delays). + super(conductor, Math.max(startTime, pendingVibratorOffDeadline), controller, + pendingVibratorOffDeadline); + this.effect = effect; + } + + @NonNull + @Override + public List<Step> play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformVendorEffectVibratorStep"); + try { + long vibratorOnResult = controller.on(effect, getVibration().id); + vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS); + handleVibratorOnResult(vibratorOnResult); + return List.of(new CompleteEffectVibratorStep(conductor, startTime, + /* cancelled= */ false, controller, mPendingVibratorOffDeadline)); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } +} diff --git a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java index f40c994d687e..901f9c3f7137 100644 --- a/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/RampOffVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.SystemClock; import android.os.Trace; import android.util.Slog; @@ -31,8 +32,7 @@ final class RampOffVibratorStep extends AbstractVibratorStep { RampOffVibratorStep(VibrationStepConductor conductor, long startTime, float amplitudeTarget, float amplitudeDelta, VibratorController controller, long pendingVibratorOffDeadline) { - super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, - pendingVibratorOffDeadline); + super(conductor, startTime, controller, pendingVibratorOffDeadline); mAmplitudeTarget = amplitudeTarget; mAmplitudeDelta = amplitudeDelta; } @@ -42,12 +42,14 @@ final class RampOffVibratorStep extends AbstractVibratorStep { return true; } + @NonNull @Override public List<Step> cancel() { return Arrays.asList(new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), controller, /* isCleanUp= */ true)); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffVibratorStep"); diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java index e13ec6c2d4ce..8478e7743183 100644 --- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.SystemClock; import android.os.Trace; import android.os.VibrationEffect; @@ -32,7 +33,7 @@ import java.util.List; * <p>This step ignores vibration completion callbacks and control the vibrator on/off state * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}. */ -final class SetAmplitudeVibratorStep extends AbstractVibratorStep { +final class SetAmplitudeVibratorStep extends AbstractComposedVibratorStep { /** * The repeating waveform keeps the vibrator ON all the time. Use a minimum duration to * prevent short patterns from turning the vibrator ON too frequently. @@ -69,6 +70,7 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep { return shouldAcceptCallback; } + @NonNull @Override public List<Step> play() { // TODO: consider separating the "on" steps at the start into a separate Step. diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java index c197271f3c7d..3ceba576fca3 100644 --- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java +++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.vibrator.IVibratorManager; import android.os.CombinedVibration; @@ -74,6 +75,7 @@ final class StartSequentialEffectStep extends Step { return mVibratorsOnMaxDuration; } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartSequentialEffectStep"); @@ -111,6 +113,7 @@ final class StartSequentialEffectStep extends Step { return nextSteps; } + @NonNull @Override public List<Step> cancel() { return VibrationStepConductor.EMPTY_STEP_LIST; @@ -173,13 +176,12 @@ final class StartSequentialEffectStep extends Step { for (int i = 0; i < vibratorCount; i++) { steps[i] = conductor.nextVibrateStep(vibrationStartTime, conductor.getVibrators().get(effectMapping.vibratorIdAt(i)), - effectMapping.effectAt(i), - /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0); + effectMapping.effectAt(i)); } if (steps.length == 1) { // No need to prepare and trigger sync effects on a single vibrator. - return startVibrating(steps[0], nextSteps); + return startVibrating(steps[0], effectMapping.effectAt(0), nextSteps); } // This synchronization of vibrators should be executed one at a time, even if we are @@ -196,8 +198,8 @@ final class StartSequentialEffectStep extends Step { effectMapping.getRequiredSyncCapabilities(), effectMapping.getVibratorIds()); - for (AbstractVibratorStep step : steps) { - long duration = startVibrating(step, nextSteps); + for (int i = 0; i < vibratorCount; i++) { + long duration = startVibrating(steps[i], effectMapping.effectAt(i), nextSteps); if (duration < 0) { // One vibrator has failed, fail this entire sync attempt. hasFailed = true; @@ -231,7 +233,12 @@ final class StartSequentialEffectStep extends Step { return hasFailed ? -1 : maxDuration; } - private long startVibrating(AbstractVibratorStep step, List<Step> nextSteps) { + private long startVibrating(@Nullable AbstractVibratorStep step, VibrationEffect effect, + List<Step> nextSteps) { + if (step == null) { + // Failed to create a step for VibrationEffect. + return -1; + } nextSteps.addAll(step.play()); long stepDuration = step.getVibratorOnDuration(); if (stepDuration < 0) { @@ -239,7 +246,7 @@ final class StartSequentialEffectStep extends Step { return stepDuration; } // Return the longest estimation for the entire effect. - return Math.max(stepDuration, step.effect.getDuration()); + return Math.max(stepDuration, effect.getDuration()); } /** @@ -249,28 +256,20 @@ final class StartSequentialEffectStep extends Step { * play all of the effects in sync. */ final class DeviceEffectMap { - private final SparseArray<VibrationEffect.Composed> mVibratorEffects; + private final SparseArray<VibrationEffect> mVibratorEffects; private final int[] mVibratorIds; private final long mRequiredSyncCapabilities; DeviceEffectMap(CombinedVibration.Mono mono) { SparseArray<VibratorController> vibrators = conductor.getVibrators(); VibrationEffect effect = mono.getEffect(); - if (effect instanceof VibrationEffect.Composed) { - mVibratorEffects = new SparseArray<>(vibrators.size()); - mVibratorIds = new int[vibrators.size()]; - - VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect; - for (int i = 0; i < vibrators.size(); i++) { - int vibratorId = vibrators.keyAt(i); - mVibratorEffects.put(vibratorId, composedEffect); - mVibratorIds[i] = vibratorId; - } - } else { - Slog.wtf(VibrationThread.TAG, - "Unable to map device vibrators to unexpected effect: " + effect); - mVibratorEffects = new SparseArray<>(); - mVibratorIds = new int[0]; + mVibratorEffects = new SparseArray<>(vibrators.size()); + mVibratorIds = new int[vibrators.size()]; + + for (int i = 0; i < vibrators.size(); i++) { + int vibratorId = vibrators.keyAt(i); + mVibratorEffects.put(vibratorId, effect); + mVibratorIds[i] = vibratorId; } mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects); } @@ -282,13 +281,7 @@ final class StartSequentialEffectStep extends Step { for (int i = 0; i < stereoEffects.size(); i++) { int vibratorId = stereoEffects.keyAt(i); if (vibrators.contains(vibratorId)) { - VibrationEffect effect = stereoEffects.valueAt(i); - if (effect instanceof VibrationEffect.Composed) { - mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect); - } else { - Slog.wtf(VibrationThread.TAG, - "Unable to map device vibrators to unexpected effect: " + effect); - } + mVibratorEffects.put(vibratorId, stereoEffects.valueAt(i)); } } mVibratorIds = new int[mVibratorEffects.size()]; @@ -326,7 +319,7 @@ final class StartSequentialEffectStep extends Step { } /** Return the {@link VibrationEffect} at given index. */ - public VibrationEffect.Composed effectAt(int index) { + public VibrationEffect effectAt(int index) { return mVibratorEffects.valueAt(index); } @@ -338,16 +331,24 @@ final class StartSequentialEffectStep extends Step { * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities. */ private long calculateRequiredSyncCapabilities( - SparseArray<VibrationEffect.Composed> effects) { + SparseArray<VibrationEffect> effects) { long prepareCap = 0; for (int i = 0; i < effects.size(); i++) { - VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0); - if (firstSegment instanceof StepSegment) { - prepareCap |= IVibratorManager.CAP_PREPARE_ON; - } else if (firstSegment instanceof PrebakedSegment) { + VibrationEffect effect = effects.valueAt(i); + if (effect instanceof VibrationEffect.VendorEffect) { prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM; - } else if (firstSegment instanceof PrimitiveSegment) { - prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE; + } else if (effect instanceof VibrationEffect.Composed composed) { + VibrationEffectSegment firstSegment = composed.getSegments().get(0); + if (firstSegment instanceof StepSegment) { + prepareCap |= IVibratorManager.CAP_PREPARE_ON; + } else if (firstSegment instanceof PrebakedSegment) { + prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM; + } else if (firstSegment instanceof PrimitiveSegment) { + prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE; + } + } else { + Slog.wtf(VibrationThread.TAG, + "Unable to check sync capabilities to unexpected effect: " + effect); } } int triggerCap = 0; diff --git a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java index 065ce1124674..87dc269532bd 100644 --- a/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/TurnOffVibratorStep.java @@ -16,6 +16,7 @@ package com.android.server.vibrator; +import android.annotation.NonNull; import android.os.SystemClock; import android.os.Trace; @@ -36,7 +37,7 @@ final class TurnOffVibratorStep extends AbstractVibratorStep { TurnOffVibratorStep(VibrationStepConductor conductor, long startTime, VibratorController controller, boolean isCleanUp) { - super(conductor, startTime, controller, /* effect= */ null, /* index= */ -1, startTime); + super(conductor, startTime, controller, startTime); mIsCleanUp = isCleanUp; } @@ -45,6 +46,7 @@ final class TurnOffVibratorStep extends AbstractVibratorStep { return mIsCleanUp; } + @NonNull @Override public List<Step> cancel() { return Arrays.asList(new TurnOffVibratorStep(conductor, SystemClock.uptimeMillis(), @@ -56,6 +58,7 @@ final class TurnOffVibratorStep extends AbstractVibratorStep { stopVibrating(); } + @NonNull @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "TurnOffVibratorStep"); diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index 689b495ec1ca..5c567da7844f 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -393,13 +393,14 @@ abstract class Vibration { private void dumpEffect( ProtoOutputStream proto, long fieldId, VibrationEffect effect) { - final long token = proto.start(fieldId); - VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; - for (VibrationEffectSegment segment : composed.getSegments()) { - dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment); + if (effect instanceof VibrationEffect.Composed composed) { + final long token = proto.start(fieldId); + for (VibrationEffectSegment segment : composed.getSegments()) { + dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment); + } + proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex()); + proto.end(token); } - proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex()); - proto.end(token); } private void dumpEffect(ProtoOutputStream proto, long fieldId, diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java index d9ca71003aae..39337594ff64 100644 --- a/services/core/java/com/android/server/vibrator/VibrationScaler.java +++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java @@ -25,14 +25,12 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; -import android.os.vibrator.VibrationEffectSegment; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Locale; /** Controls vibration scaling. */ @@ -136,12 +134,6 @@ final class VibrationScaler { */ @NonNull public VibrationEffect scale(@NonNull VibrationEffect effect, int usageHint) { - if (!(effect instanceof VibrationEffect.Composed)) { - // This only scales composed vibration effects. - Slog.wtf(TAG, "Error scaling unsupported vibration effect: " + effect); - return effect; - } - int newEffectStrength = getEffectStrength(usageHint); ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint)); float adaptiveScale = getAdaptiveHapticsScale(usageHint); @@ -154,26 +146,10 @@ final class VibrationScaler { scaleLevel = SCALE_LEVEL_NONE; } - VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect; - ArrayList<VibrationEffectSegment> segments = - new ArrayList<>(composedEffect.getSegments()); - int segmentCount = segments.size(); - for (int i = 0; i < segmentCount; i++) { - segments.set(i, - segments.get(i).resolve(mDefaultVibrationAmplitude) - .applyEffectStrength(newEffectStrength) - .scale(scaleLevel.factor) - .scaleLinearly(adaptiveScale)); - } - if (segments.equals(composedEffect.getSegments())) { - // No segment was updated, return original effect. - return effect; - } - VibrationEffect.Composed scaled = - new VibrationEffect.Composed(segments, composedEffect.getRepeatIndex()); - // Make sure we validate what was scaled, since we're using the constructor directly - scaled.validate(); - return scaled; + return effect.resolve(mDefaultVibrationAmplitude) + .applyEffectStrength(newEffectStrength) + .scale(scaleLevel.factor) + .scaleLinearly(adaptiveScale); } /** diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index f3e226e09447..8c9a92de03a9 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -123,6 +123,24 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { @Nullable AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller, + VibrationEffect effect) { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + if (effect instanceof VibrationEffect.VendorEffect vendorEffect) { + return new PerformVendorEffectVibratorStep(this, startTime, controller, vendorEffect, + /* pendingVibratorOffDeadline= */ 0); + } + if (effect instanceof VibrationEffect.Composed composed) { + return nextVibrateStep(startTime, controller, composed, /* segmentIndex= */ 0, + /* pendingVibratorOffDeadline= */ 0); + } + Slog.wtf(TAG, "Unable to create next step for unexpected effect: " + effect); + return null; + } + + @NonNull + AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline) { if (Build.IS_DEBUGGABLE) { expectIsVibrationThread(true); diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 988e8fea70b9..8cc157c2ed81 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -20,8 +20,10 @@ import android.annotation.Nullable; import android.hardware.vibrator.IVibrator; import android.os.Binder; import android.os.IVibratorStateListener; +import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; @@ -262,6 +264,35 @@ final class VibratorController { } /** + * Plays vendor vibration effect, using {@code vibrationId} for completion callback to + * {@link OnVibrationCompleteListener}. + * + * <p>This will affect the state of {@link #isVibrating()}. + * + * @return The positive duration of the vibration started, if successful, zero if the vibrator + * do not support the input or a negative number if the operation failed. + */ + public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) { + synchronized (mLock) { + Parcel vendorData = Parcel.obtain(); + try { + vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0); + vendorData.setDataPosition(0); + long duration = mNativeWrapper.performVendorEffect(vendorData, + vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(), + vibrationId); + if (duration > 0) { + mCurrentAmplitude = -1; + notifyListenerOnVibrating(true); + } + return duration; + } finally { + vendorData.recycle(); + } + } + } + + /** * Plays predefined vibration effect, using {@code vibrationId} for completion callback to * {@link OnVibrationCompleteListener}. * @@ -427,6 +458,9 @@ final class VibratorController { private static native long performEffect(long nativePtr, long effect, long strength, long vibrationId); + private static native long performVendorEffect(long nativePtr, Parcel vendorData, + long strength, float scale, long vibrationId); + private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect, long vibrationId); @@ -482,6 +516,12 @@ final class VibratorController { return performEffect(mNativePtr, effect, strength, vibrationId); } + /** Turns vibrator on to perform a vendor-specific effect. */ + public long performVendorEffect(Parcel vendorData, long strength, float scale, + long vibrationId) { + return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId); + } + /** Turns vibrator on to perform effect composed of give primitives effect. */ public long compose(PrimitiveSegment[] primitives, long vibrationId) { return performComposedEffect(mNativePtr, primitives, vibrationId); diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index bff175fec1dd..48c4a68250b1 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -540,6 +540,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Slog.e(TAG, "token must not be null"); return null; } + if (effect.hasVendorEffects() + && !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) { + Slog.w(TAG, "vibrate; no permission for vendor effects"); + return null; + } enforceUpdateAppOpsStatsPermission(uid); if (!isEffectValid(effect)) { return null; @@ -1304,12 +1309,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private void fillVibrationFallbacks(HalVibration vib, VibrationEffect effect) { - VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + if (!(effect instanceof VibrationEffect.Composed composed)) { + return; + } int segmentCount = composed.getSegments().size(); for (int i = 0; i < segmentCount; i++) { VibrationEffectSegment segment = composed.getSegments().get(i); - if (segment instanceof PrebakedSegment) { - PrebakedSegment prebaked = (PrebakedSegment) segment; + if (segment instanceof PrebakedSegment prebaked) { VibrationEffect fallback = mVibrationSettings.getFallbackEffect( prebaked.getEffectId()); if (prebaked.shouldFallback() && fallback != null) { @@ -1392,12 +1398,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Nullable private static PrebakedSegment extractPrebakedSegment(VibrationEffect effect) { - if (effect instanceof VibrationEffect.Composed) { - VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; + if (effect instanceof VibrationEffect.Composed composed) { if (composed.getSegments().size() == 1) { VibrationEffectSegment segment = composed.getSegments().get(0); - if (segment instanceof PrebakedSegment) { - return (PrebakedSegment) segment; + if (segment instanceof PrebakedSegment prebaked) { + return prebaked; } } } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index a8dcaa8a90e1..023dd79d391b 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1124,8 +1124,8 @@ class ActivityClientController extends IActivityClientController.Stub { } try { - mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), - EnterPipRequestedItem.obtain(r.token)); + final EnterPipRequestedItem item = new EnterPipRequestedItem(r.token); + mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), item); return true; } catch (Exception e) { Slog.w(TAG, "Failed to send enter pip requested item: " @@ -1140,8 +1140,8 @@ class ActivityClientController extends IActivityClientController.Stub { void onPictureInPictureUiStateChanged(@NonNull ActivityRecord r, PictureInPictureUiState pipState) { try { - mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), - PipStateTransactionItem.obtain(r.token, pipState)); + final PipStateTransactionItem item = new PipStateTransactionItem(r.token, pipState); + mService.getLifecycleManager().scheduleTransactionItem(r.app.getThread(), item); } catch (Exception e) { Slog.w(TAG, "Failed to send pip state transaction item: " + r.intent.getComponent(), e); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index ee4deae21d4f..d039b048a3e4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1467,8 +1467,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId, config); - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - MoveToDisplayItem.obtain(token, displayId, config, activityWindowInfo)); + final MoveToDisplayItem item = + new MoveToDisplayItem(token, displayId, config, activityWindowInfo); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); } catch (RemoteException e) { // If process died, whatever. } @@ -1485,8 +1486,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, " + "config: %s", this, config); - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - ActivityConfigurationChangeItem.obtain(token, config, activityWindowInfo)); + final ActivityConfigurationChangeItem item = + new ActivityConfigurationChangeItem(token, config, activityWindowInfo); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); } catch (RemoteException e) { // If process died, whatever. } @@ -1506,8 +1508,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STATES, "Sending position change to %s, onTop: %b", this, onTop); - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - TopResumedActivityChangeItem.obtain(token, onTop)); + final TopResumedActivityChangeItem item = + new TopResumedActivityChangeItem(token, onTop); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); } catch (RemoteException e) { // If process died, whatever. Slog.w(TAG, "Failed to send top-resumed=" + onTop + " to " + this, e); @@ -2810,9 +2813,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } try { mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT; - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - TransferSplashScreenViewStateItem.obtain(token, parcelable, - windowAnimationLeash)); + final TransferSplashScreenViewStateItem item = + new TransferSplashScreenViewStateItem(token, parcelable, windowAnimationLeash); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); scheduleTransferSplashScreenTimeout(); } catch (Exception e) { Slog.w(TAG, "onCopySplashScreenComplete fail: " + this); @@ -4993,8 +4996,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { final ArrayList<ResultInfo> list = new ArrayList<>(); list.add(new ResultInfo(resultWho, requestCode, resultCode, data, callerToken)); - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - ActivityResultItem.obtain(token, list)); + final ActivityResultItem item = new ActivityResultItem(token, list); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); return; } catch (Exception e) { Slog.w(TAG, "Exception thrown sending result to " + this, e); @@ -5005,9 +5008,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED, STOPPING, STOPPED)) { // Build result to be returned immediately. - final ActivityResultItem activityResultItem = ActivityResultItem.obtain( - token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data, - callerToken))); + final List<ResultInfo> infos = List.of( + new ResultInfo(resultWho, requestCode, resultCode, data, callerToken)); + final ActivityResultItem activityResultItem = new ActivityResultItem(token, infos); // When the activity result is delivered, the activity will transition to RESUMED. // Since the activity is only resumed so the result can be immediately delivered, // return it to its original lifecycle state. @@ -5050,13 +5053,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private ActivityLifecycleItem getLifecycleItemForCurrentStateForResult() { switch (mState) { case STARTED: - return StartActivityItem.obtain(token, null); + return new StartActivityItem(token, null); case PAUSING: case PAUSED: - return PauseActivityItem.obtain(token); + return new PauseActivityItem(token); case STOPPING: case STOPPED: - return StopActivityItem.obtain(token); + return new StopActivityItem(token); default: // Do not send a result immediately if the activity is in state INITIALIZING, // RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED. @@ -5111,8 +5114,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Making sure the client state is RESUMED after transaction completed and doing // so only if activity is currently RESUMED. Otherwise, client may have extra // life-cycle calls to RESUMED (and PAUSED later). - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - NewIntentItem.obtain(token, ar, mState == RESUMED)); + final NewIntentItem item = + new NewIntentItem(token, ar, mState == RESUMED /* resume */); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); unsent = false; } catch (RemoteException e) { Slog.w(TAG, "Exception thrown sending new intent to " + this, e); @@ -6344,9 +6348,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A EventLogTags.writeWmPauseActivity(mUserId, System.identityHashCode(this), shortComponentName, "userLeaving=false", "make-active"); try { - mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - PauseActivityItem.obtain(token, finishing, false /* userLeaving */, - false /* dontReport */, mAutoEnteringPip)); + final PauseActivityItem item = new PauseActivityItem(token, finishing, + false /* userLeaving */, false /* dontReport */, mAutoEnteringPip); + mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), item); } catch (Exception e) { Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e); } @@ -6358,7 +6362,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - StartActivityItem.obtain(token, takeSceneTransitionInfo())); + new StartActivityItem(token, takeSceneTransitionInfo())); } catch (Exception e) { Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e); } @@ -6655,7 +6659,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A EventLogTags.writeWmStopActivity( mUserId, System.identityHashCode(this), shortComponentName); mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - StopActivityItem.obtain(token)); + new StopActivityItem(token)); mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT); } catch (Exception e) { @@ -9990,17 +9994,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A try { ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" , (andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6)); - final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(token, + final ClientTransactionItem callbackItem = new ActivityRelaunchItem(token, pendingResults, pendingNewIntents, configChangeFlags, new MergedConfiguration(getProcessGlobalConfiguration(), getMergedOverrideConfiguration()), preserveWindow, getActivityWindowInfo()); final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(token, isTransitionForward(), + lifecycleItem = new ResumeActivityItem(token, isTransitionForward(), shouldSendCompatFakeFocus()); } else { - lifecycleItem = PauseActivityItem.obtain(token); + lifecycleItem = new PauseActivityItem(token); } mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( app.getThread(), callbackItem, lifecycleItem); @@ -10092,7 +10096,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // {@link ActivityTaskManagerService.activityStopped}). try { mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(), - StopActivityItem.obtain(token)); + new StopActivityItem(token)); } catch (RemoteException e) { Slog.w(TAG, "Exception thrown during restart " + this, e); } @@ -10886,12 +10890,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Whether we should send fake focus when the activity is resumed. This is done because some * game engines wait to get focus before drawing the content of the app. */ - // TODO(b/263593361): Explore enabling compat fake focus for freeform. - // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when - // covered with bubbles. boolean shouldSendCompatFakeFocus() { - return mLetterboxUiController.shouldSendFakeFocus() && inMultiWindowMode() - && !inPinnedWindowingMode() && !inFreeformWindowingMode(); + return mAppCompatController.getAppCompatFocusOverrides().shouldSendFakeFocus(); } boolean canCaptureSnapshot() { diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java index bc822718d45a..dcc325eae702 100644 --- a/services/core/java/com/android/server/wm/ActivityRefresher.java +++ b/services/core/java/com/android/server/wm/ActivityRefresher.java @@ -84,9 +84,9 @@ class ActivityRefresher { ProtoLog.v(WM_DEBUG_STATES, "Refreshing activity for freeform camera compatibility treatment, " + "activityRecord=%s", activity); - final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain( - activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( + final RefreshCallbackItem refreshCallbackItem = + new RefreshCallbackItem(activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); try { activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e81b440f6d6d..b0d8925efcfe 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -929,7 +929,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final boolean isTransitionForward = r.isTransitionForward(); final IBinder fragmentToken = r.getTaskFragment().getFragmentToken(); final int deviceId = getDeviceIdForDisplayId(r.getDisplayId()); - final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token, + final LaunchActivityItem launchActivityItem = new LaunchActivityItem(r.token, r.intent, System.identityHashCode(r), r.info, procConfig, overrideConfig, deviceId, r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor, @@ -942,12 +942,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(r.token, isTransitionForward, + lifecycleItem = new ResumeActivityItem(r.token, isTransitionForward, r.shouldSendCompatFakeFocus()); } else if (r.isVisibleRequested()) { - lifecycleItem = PauseActivityItem.obtain(r.token); + lifecycleItem = new PauseActivityItem(r.token); } else { - lifecycleItem = StopActivityItem.obtain(r.token); + lifecycleItem = new StopActivityItem(r.token); } // Schedule transaction. diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java index f9e2507aa1eb..998d65d84b3d 100644 --- a/services/core/java/com/android/server/wm/AppCompatController.java +++ b/services/core/java/com/android/server/wm/AppCompatController.java @@ -94,4 +94,9 @@ class AppCompatController { } return null; } + + @NonNull + AppCompatFocusOverrides getAppCompatFocusOverrides() { + return mAppCompatOverrides.getAppCompatFocusOverrides(); + } } diff --git a/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java new file mode 100644 index 000000000000..ab4bb140f3a6 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppCompatFocusOverrides.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 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.server.wm; + +import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; +import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; + +import static com.android.server.wm.AppCompatUtils.isChangeEnabled; + +import android.annotation.NonNull; + +import com.android.server.wm.utils.OptPropFactory; + +/** + * Encapsulates app compat focus policy. + */ +class AppCompatFocusOverrides { + + @NonNull + final ActivityRecord mActivityRecord; + @NonNull + private final OptPropFactory.OptProp mFakeFocusOptProp; + + AppCompatFocusOverrides(@NonNull ActivityRecord activityRecord, + @NonNull AppCompatConfiguration appCompatConfiguration, + @NonNull OptPropFactory optPropBuilder) { + mActivityRecord = activityRecord; + mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, + appCompatConfiguration::isCompatFakeFocusEnabled); + } + + /** + * Whether sending compat fake focus for split screen resumed activities is enabled. Needed + * because some game engines wait to get focus before drawing the content of the app which isn't + * guaranteed by default in multi-window modes. + * + * <p>This treatment is enabled when the following conditions are met: + * <ul> + * <li>Flag gating the treatment is enabled + * <li>Component property is NOT set to false + * <li>Component property is set to true or per-app override is enabled + * </ul> + */ + boolean shouldSendFakeFocus() { + // TODO(b/263593361): Explore enabling compat fake focus for freeform. + // TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when + // covered with bubbles. + return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty( + isChangeEnabled(mActivityRecord, OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)) + && mActivityRecord.inMultiWindowMode() && !mActivityRecord.inPinnedWindowingMode() + && !mActivityRecord.inFreeformWindowingMode(); + } + +} diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java index b611ba9bb065..cde48d64d398 100644 --- a/services/core/java/com/android/server/wm/AppCompatOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java @@ -18,17 +18,12 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP; import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; -import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_RESPECT_REQUESTED_ORIENTATION; import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; -import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; - -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.NonNull; @@ -39,17 +34,10 @@ import com.android.server.wm.utils.OptPropFactory; */ public class AppCompatOverrides { - private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatOverrides" : TAG_ATM; - - @NonNull - private final AppCompatConfiguration mAppCompatConfiguration; - @NonNull private final ActivityRecord mActivityRecord; @NonNull - private final OptPropFactory.OptProp mFakeFocusOptProp; - @NonNull private final OptPropFactory.OptProp mAllowOrientationOverrideOptProp; @NonNull private final OptPropFactory.OptProp mAllowDisplayOrientationOverrideOptProp; @@ -61,26 +49,25 @@ public class AppCompatOverrides { private final AppCompatCameraOverrides mAppCompatCameraOverrides; @NonNull private final AppCompatAspectRatioOverrides mAppCompatAspectRatioOverrides; + @NonNull + private final AppCompatFocusOverrides mAppCompatFocusOverrides; AppCompatOverrides(@NonNull ActivityRecord activityRecord, @NonNull AppCompatConfiguration appCompatConfiguration, @NonNull OptPropFactory optPropBuilder) { - mAppCompatConfiguration = appCompatConfiguration; mActivityRecord = activityRecord; mAppCompatCameraOverrides = new AppCompatCameraOverrides(mActivityRecord, - mAppCompatConfiguration, optPropBuilder); + appCompatConfiguration, optPropBuilder); mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(mActivityRecord, - mAppCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides); + appCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides); // TODO(b/341903757) Remove BooleanSuppliers after fixing dependency with reachability. mAppCompatAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord, - mAppCompatConfiguration, optPropBuilder, + appCompatConfiguration, optPropBuilder, activityRecord.mLetterboxUiController::isDisplayFullScreenAndInPosture, activityRecord.mLetterboxUiController::getHorizontalPositionMultiplier); - - mFakeFocusOptProp = optPropBuilder.create(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, - mAppCompatConfiguration::isCompatFakeFocusEnabled); - + mAppCompatFocusOverrides = new AppCompatFocusOverrides(mActivityRecord, + appCompatConfiguration, optPropBuilder); mAllowOrientationOverrideOptProp = optPropBuilder.create( PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE); @@ -114,21 +101,9 @@ public class AppCompatOverrides { return mAppCompatAspectRatioOverrides; } - /** - * Whether sending compat fake focus for split screen resumed activities is enabled. Needed - * because some game engines wait to get focus before drawing the content of the app which isn't - * guaranteed by default in multi-window modes. - * - * <p>This treatment is enabled when the following conditions are met: - * <ul> - * <li>Flag gating the treatment is enabled - * <li>Component property is NOT set to false - * <li>Component property is set to true or per-app override is enabled - * </ul> - */ - boolean shouldSendFakeFocus() { - return mFakeFocusOptProp.shouldEnableWithOverrideAndProperty( - isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)); + @NonNull + AppCompatFocusOverrides getAppCompatFocusOverrides() { + return mAppCompatFocusOverrides; } boolean isAllowOrientationOverrideOptOut() { diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java index 1b30a20d3e9a..fd816067fbad 100644 --- a/services/core/java/com/android/server/wm/AppCompatUtils.java +++ b/services/core/java/com/android/server/wm/AppCompatUtils.java @@ -73,4 +73,13 @@ class AppCompatUtils { static boolean isInVrUiMode(Configuration config) { return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET; } + + /** + * @param activityRecord The {@link ActivityRecord} for the app package. + * @param overrideChangeId The per-app override identifier. + * @return {@code true} if the per-app override is enable for the given activity. + */ + static boolean isChangeEnabled(@NonNull ActivityRecord activityRecord, long overrideChangeId) { + return activityRecord.info.isChangeEnabled(overrideChangeId); + } } diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java index 3ecdff6b18a0..cbb210f514a0 100644 --- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java +++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java @@ -27,6 +27,7 @@ import static com.android.server.wm.AppCompatUtils.computeAspectRatio; import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity; import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -41,7 +42,7 @@ import android.view.Gravity; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.window.flags.Flags; +import com.android.server.wm.utils.DesktopModeFlagsUtil; import java.util.function.Consumer; @@ -106,7 +107,7 @@ public final class DesktopModeBoundsCalculator { final TaskDisplayArea displayArea = task.getDisplayArea(); final Rect screenBounds = displayArea.getBounds(); final Size idealSize = calculateIdealSize(screenBounds, DESKTOP_MODE_INITIAL_BOUNDS_SCALE); - if (!Flags.enableWindowingDynamicInitialBounds()) { + if (!DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(activity.mWmService.mContext)) { return centerInScreen(idealSize, screenBounds); } // TODO(b/353457301): Replace with app compat aspect ratio method when refactoring complete. diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 7c31177e98f6..2d1eb419a0bf 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -28,7 +28,13 @@ import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.window.flags.Flags; + +/** + * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is + * black layers of varying opacity at various Z-levels which create the effect of a Dim. + */ class Dimmer { /** @@ -122,8 +128,10 @@ class Dimmer { /** * Set the parameters to prepare the dim to be relative parented to the dimming container */ - void prepareReparent(@NonNull WindowContainer<?> relativeParent, int relativeLayer) { + void prepareReparent(@NonNull WindowContainer<?> geometryParent, + @NonNull WindowContainer<?> relativeParent, int relativeLayer) { mAnimationHelper.setRequestedRelativeParent(relativeParent, relativeLayer); + mAnimationHelper.setRequestedGeometryParent(geometryParent); } /** @@ -138,7 +146,8 @@ class Dimmer { * Whether anyone is currently requesting the dim */ boolean isDimming() { - return mLastRequestedDimContainer != null; + return mLastRequestedDimContainer != null + && (mHostContainer.isVisibleRequested() || !Flags.useTasksDimOnly()); } private SurfaceControl makeDimLayer() { @@ -208,13 +217,15 @@ class Dimmer { * the child of the host should call adjustRelativeLayer and {@link Dimmer#adjustAppearance} to * continue dimming. Indeed, this method won't be able to keep dimming or get a new DimState * without also adjusting the appearance. + * @param geometryParent The container that defines the geometry of the dim * @param dimmingContainer The container which to dim above. Should be a child of the host. * @param relativeLayer The position of the dim wrt the container */ - public void adjustRelativeLayer(@NonNull WindowContainer<?> dimmingContainer, + public void adjustPosition(@NonNull WindowContainer<?> geometryParent, + @NonNull WindowContainer<?> dimmingContainer, int relativeLayer) { if (mDimState != null) { - mDimState.prepareReparent(dimmingContainer, relativeLayer); + mDimState.prepareReparent(geometryParent, dimmingContainer, relativeLayer); } } @@ -236,7 +247,9 @@ class Dimmer { return false; } else { // Someone is dimming, show the requested changes - mDimState.adjustSurfaceLayout(t); + if (!Flags.useTasksDimOnly()) { + mDimState.adjustSurfaceLayout(t); + } final WindowState ws = mDimState.mLastRequestedDimContainer.asWindowState(); if (!mDimState.mIsVisible && ws != null && ws.mActivityRecord != null && ws.mActivityRecord.mStartingData != null) { @@ -263,6 +276,7 @@ class Dimmer { return mDimState != null ? mDimState.mDimSurface : null; } + @Deprecated Rect getDimBounds() { return mDimState != null ? mDimState.mDimBounds : null; } diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java index df1549e6a0f0..3dba57f8c4cd 100644 --- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java +++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java @@ -26,6 +26,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -48,6 +49,7 @@ public class DimmerAnimationHelper { private float mAlpha = -1f; private int mBlurRadius = -1; private WindowContainer<?> mDimmingContainer = null; + private WindowContainer<?> mGeometryParent = null; private int mRelativeLayer = -1; private static final float EPSILON = 0.0001f; @@ -103,6 +105,11 @@ public class DimmerAnimationHelper { mRequestedProperties.mRelativeLayer = relativeLayer; } + // Sets the requested layer to reparent the dim to without applying it immediately + void setRequestedGeometryParent(WindowContainer<?> geometryParent) { + mRequestedProperties.mGeometryParent = geometryParent; + } + // Sets a requested change without applying it immediately void setRequestedAppearance(float alpha, int blurRadius) { mRequestedProperties.mAlpha = alpha; @@ -129,7 +136,9 @@ public class DimmerAnimationHelper { } dim.ensureVisible(t); - relativeReparent(dim.mDimSurface, + reparent(dim.mDimSurface, + mRequestedProperties.mGeometryParent != mCurrentProperties.mGeometryParent + ? mRequestedProperties.mGeometryParent.getSurfaceControl() : null, mRequestedProperties.mDimmingContainer.getSurfaceControl(), mRequestedProperties.mRelativeLayer, t); @@ -214,11 +223,17 @@ public class DimmerAnimationHelper { } /** - * Change the relative parent of this dim layer + * Change the geometry and relative parent of this dim layer */ - void relativeReparent(@NonNull SurfaceControl dimLayer, @NonNull SurfaceControl relativeParent, - int relativePosition, @NonNull SurfaceControl.Transaction t) { + void reparent(@NonNull SurfaceControl dimLayer, + @Nullable SurfaceControl newGeometryParent, + @NonNull SurfaceControl relativeParent, + int relativePosition, + @NonNull SurfaceControl.Transaction t) { try { + if (newGeometryParent != null) { + t.reparent(dimLayer, newGeometryParent); + } t.setRelativeLayer(dimLayer, relativeParent, relativePosition); } catch (NullPointerException e) { Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1efb3ef144d9..475b4737fd6b 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -550,7 +550,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Save allocating when calculating rects */ private final Rect mTmpRect = new Rect(); - private final Rect mTmpRect2 = new Rect(); private final Region mTmpRegion = new Region(); private final Configuration mTmpConfiguration = new Configuration(); diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index be8e806a5752..73f36557f3d6 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -117,22 +117,6 @@ final class LetterboxUiController { } /** - * Whether sending compat fake focus for split screen resumed activities is enabled. Needed - * because some game engines wait to get focus before drawing the content of the app which isn't - * guaranteed by default in multi-window modes. - * - * <p>This treatment is enabled when the following conditions are met: - * <ul> - * <li>Flag gating the treatment is enabled - * <li>Component property is NOT set to false - * <li>Component property is set to true or per-app override is enabled - * </ul> - */ - boolean shouldSendFakeFocus() { - return getAppCompatOverrides().shouldSendFakeFocus(); - } - - /** * Whether we should apply the force resize per-app override. When this override is applied it * forces the packages it is applied to to be resizable. It won't change whether the app can be * put into multi-windowing mode, but allow the app to resize without going into size-compat diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 259ca83f47e0..74adeb935a94 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3201,6 +3201,14 @@ class Task extends TaskFragment { return "Task=" + mTaskId; } + WindowContainer<?> getDimmerParent() { + if (!inMultiWindowMode() && isTranslucentForTransition()) { + return getRootDisplayArea(); + } + return this; + } + + @Deprecated @Override Dimmer getDimmer() { // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 29e82f7fe88f..c83b28055a2f 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1610,18 +1610,15 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); } - final ActivityResultItem activityResultItem = ActivityResultItem.obtain( - next.token, a); - mAtmService.getLifecycleManager().scheduleTransactionItem( - appThread, activityResultItem); + final ActivityResultItem item = new ActivityResultItem(next.token, a); + mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item); } } if (next.newIntents != null) { - final NewIntentItem newIntentItem = NewIntentItem.obtain( - next.token, next.newIntents, true /* resume */); - mAtmService.getLifecycleManager().scheduleTransactionItem( - appThread, newIntentItem); + final NewIntentItem item = + new NewIntentItem(next.token, next.newIntents, true /* resume */); + mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item); } // Well the app will no longer be stopped. @@ -1635,7 +1632,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { final int topProcessState = mAtmService.mTopProcessState; next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState); next.abortAndClearOptionsAnimation(); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( next.token, topProcessState, dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()); mAtmService.getLifecycleManager().scheduleTransactionItem( @@ -1881,9 +1878,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving, reason); - mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(), - PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving, - pauseImmediately, autoEnteringPip)); + final PauseActivityItem item = new PauseActivityItem(prev.token, prev.finishing, + userLeaving, pauseImmediately, autoEnteringPip); + mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(), item); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); @@ -3105,6 +3102,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return forAllWindows(getDimBehindWindow, true); } + @Deprecated @Override Dimmer getDimmer() { // If this is in an embedded TaskFragment and we want the dim applies on the TaskFragment. diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 57c7753d5c4b..235deb206928 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -232,8 +232,14 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot if (imeWindow != null && imeWindow.isVisible()) { final Rect bounds = imeWindow.getParentFrame(); bounds.offsetTo(0, 0); - imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(), - bounds, 1.0f, pixelFormat, null); + ScreenCapture.LayerCaptureArgs captureArgs = new ScreenCapture.LayerCaptureArgs.Builder( + imeWindow.getSurfaceControl()) + .setSourceCrop(bounds) + .setFrameScale(1.0f) + .setPixelFormat(pixelFormat) + .setCaptureSecureLayers(true) + .build(); + imeBuffer = ScreenCapture.captureLayers(captureArgs); } return imeBuffer; } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index cf5a1e61d450..358adc3352e7 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -463,14 +463,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { boolean canApplyDim(@NonNull Task task) { if (mTransientLaunches == null) return true; - final Dimmer dimmer = task.getDimmer(); - if (dimmer == null) { - return false; - } - if (dimmer.hostIsTask()) { + if (Flags.useTasksDimOnly()) { + WindowContainer<?> dimmerParent = task.getDimmerParent(); + if (dimmerParent == null) { + return false; + } // Always allow to dim if the host only affects its task. - return true; + if (dimmerParent.asTask() == task) { + return true; + } + } else { + final Dimmer dimmer = task.getDimmer(); + if (dimmer == null) { + return false; + } + if (dimmer.hostIsTask()) { + // Always allow to dim if the host only affects its task. + return true; + } } + // The dimmer host of a translucent task can be a display, then it is not in transient-hide. for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { // The transient task is usually the task of recents/home activity. diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index eb1a80b74b76..64f9c012ea28 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -107,11 +107,10 @@ import android.view.animation.Animation; import android.window.IWindowContainerToken; import android.window.WindowContainerToken; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; -import com.android.internal.protolog.common.LogLevel; import com.android.internal.protolog.ProtoLog; +import com.android.internal.protolog.common.LogLevel; import com.android.internal.util.ToBooleanFunction; import com.android.server.wm.SurfaceAnimator.Animatable; import com.android.server.wm.SurfaceAnimator.AnimationType; @@ -3765,6 +3764,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< }, true /* traverseTopToBottom */); } + @Deprecated Dimmer getDimmer() { if (mParent == null) { return null; diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java index cd785e5174e5..87e120c9a15d 100644 --- a/services/core/java/com/android/server/wm/WindowContextListenerController.java +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -330,8 +330,8 @@ class WindowContextListenerController { mLastReportedConfig.setTo(config); mLastReportedDisplay = displayId; - mWpc.scheduleClientTransactionItem(WindowContextInfoChangeItem.obtain( - mClientToken, config, displayId)); + mWpc.scheduleClientTransactionItem( + new WindowContextInfoChangeItem(mClientToken, config, displayId)); mHasPendingConfiguration = false; } @@ -356,7 +356,7 @@ class WindowContextListenerController { } } mDeathRecipient.unlinkToDeath(); - mWpc.scheduleClientTransactionItem(WindowContextWindowRemovalItem.obtain(mClientToken)); + mWpc.scheduleClientTransactionItem(new WindowContextWindowRemovalItem(mClientToken)); unregister(); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 984caf1c692b..2bae0a826417 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -437,7 +437,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio final ConfigurationChangeItem configurationChangeItem; synchronized (mLastReportedConfiguration) { onConfigurationChangePreScheduled(mLastReportedConfiguration); - configurationChangeItem = ConfigurationChangeItem.obtain( + configurationChangeItem = new ConfigurationChangeItem( mLastReportedConfiguration, mLastTopActivityDeviceId); } // Schedule immediately to make sure the app component (e.g. receiver, service) can get @@ -1721,8 +1721,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } onConfigurationChangePreScheduled(config); - scheduleClientTransactionItem(thread, ConfigurationChangeItem.obtain( - config, mLastTopActivityDeviceId)); + scheduleClientTransactionItem( + thread, new ConfigurationChangeItem(config, mLastTopActivityDeviceId)); } private void onConfigurationChangePreScheduled(@NonNull Configuration config) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 240978ac96a0..164994c6513e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -103,6 +103,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; @@ -5195,9 +5196,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } private void applyDims() { + Task task = getTask(); if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind()) && mWinAnimator.getShown() - && !mHidden && mTransitionController.canApplyDim(getTask())) { + && !mHidden && mTransitionController.canApplyDim(task)) { // Only show the Dimmer when the following is satisfied: // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting. @@ -5210,10 +5212,31 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // If the window is visible from surface flinger perspective (mWinAnimator.getShown()) // but not window manager visible (!isVisibleNow()), it can still be the parent of the // dim, but can not create a new surface or continue a dim alone. + Dimmer dimmer; + WindowContainer<?> geometryParent = task; + if (Flags.useTasksDimOnly()) { + if (task != null) { + geometryParent = task.getDimmerParent(); + dimmer = task.mDimmer; + } else { + RootDisplayArea displayArea = getRootDisplayArea(); + geometryParent = displayArea; + dimmer = displayArea != null ? displayArea.getDimmer() : null; + } + if (dimmer == null) { + ProtoLog.e(WM_DEBUG_DIMMER, "WindowState %s does not have task or" + + " display area for dimming", this); + return; + } + } else { + dimmer = getDimmer(); + } + if (isVisibleNow()) { - getDimmer().adjustAppearance(this, dimAmount, blurRadius); + dimmer.adjustAppearance(this, dimAmount, blurRadius); } - getDimmer().adjustRelativeLayer(this, -1 /* relativeLayer */); + dimmer.adjustPosition(geometryParent, + this /* relativeParent */, -1 /* relativeLayer */); } } diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java index 3559e620a350..70c66de22ddf 100644 --- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java +++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java @@ -43,8 +43,7 @@ public enum DesktopModeFlagsUtil { // All desktop mode related flags to be overridden by developer option toggle will be added here DESKTOP_WINDOWING_MODE( Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true), - WALLPAPER_ACTIVITY( - Flags::enableDesktopWindowingWallpaperActivity, /* shouldOverrideByDevOption= */ true); + DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true); private static final String TAG = "DesktopModeFlagsUtil"; // Function called to obtain aconfig flag value. diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 3cd5f7683ac8..9fa1a53237cc 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -193,7 +193,7 @@ cc_defaults { "android.hardware.thermal-V2-ndk", "android.hardware.tv.input@1.0", "android.hardware.tv.input-V2-ndk", - "android.hardware.vibrator-V2-ndk", + "android.hardware.vibrator-V3-ndk", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 2804a10c317f..f12930a49ecb 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -17,7 +17,10 @@ #define LOG_TAG "VibratorController" #include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/binder_parcel.h> +#include <android/binder_parcel_jni.h> #include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/persistable_bundle_aidl.h> #include <nativehelper/JNIHelp.h> #include <utils/Log.h> #include <utils/misc.h> @@ -32,6 +35,8 @@ namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_3 = android::hardware::vibrator::V1_3; namespace Aidl = aidl::android::hardware::vibrator; +using aidl::android::os::PersistableBundle; + namespace android { static JavaVM* sJvm = nullptr; @@ -95,7 +100,7 @@ static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) return nullptr; } auto result = manager->getVibrator(vibratorId); - return result.isOk() ? std::move(result.value()) : nullptr; + return result.isOk() ? result.value() : nullptr; } class VibratorControllerWrapper { @@ -192,6 +197,29 @@ static Aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primit return effect; } +static Aidl::VendorEffect vendorEffectFromJavaParcel(JNIEnv* env, jobject vendorData, + jlong strength, jfloat scale) { + PersistableBundle bundle; + if (AParcel* parcel = AParcel_fromJavaParcel(env, vendorData); parcel != nullptr) { + if (binder_status_t status = bundle.readFromParcel(parcel); status == STATUS_OK) { + AParcel_delete(parcel); + } else { + jniThrowExceptionFmt(env, "android/os/BadParcelableException", + "Failed to readFromParcel, status %d (%s)", status, + strerror(-status)); + } + } else { + jniThrowExceptionFmt(env, "android/os/BadParcelableException", + "Failed to AParcel_fromJavaParcel, for nullptr"); + } + + Aidl::VendorEffect effect; + effect.vendorData = bundle; + effect.strength = static_cast<Aidl::EffectStrength>(strength); + effect.scale = static_cast<float>(scale); + return effect; +} + static void destroyNativeWrapper(void* ptr) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper) { @@ -289,6 +317,23 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j return result.isOk() ? result.value().count() : (result.isUnsupported() ? 0 : -1); } +static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, + jobject vendorData, jlong strength, jfloat scale, + jlong vibrationId) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized"); + return -1; + } + Aidl::VendorEffect effect = vendorEffectFromJavaParcel(env, vendorData, strength, scale); + auto callback = wrapper->createCallback(vibrationId); + auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) { + return hal->performVendorEffect(effect, callback); + }; + auto result = wrapper->halCall<void>(performVendorEffectFn, "performVendorEffect"); + return result.isOk() ? std::numeric_limits<int64_t>::max() : (result.isUnsupported() ? 0 : -1); +} + static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jobjectArray composition, jlong vibrationId) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); @@ -466,6 +511,7 @@ static const JNINativeMethod method_table[] = { {"off", "(J)V", (void*)vibratorOff}, {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude}, {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, + {"performVendorEffect", "(JLandroid/os/Parcel;JFJ)J", (void*)vibratorPerformVendorEffect}, {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J", (void*)vibratorPerformComposedEffect}, {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J", diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index e6dac88f8a9e..dab397864613 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -354,6 +354,9 @@ public final class ProfcollectForwardingService extends SystemService { private static void createAndUploadReport(ProfcollectForwardingService pfs) { BackgroundThread.get().getThreadHandler().post(() -> { + if (pfs.mIProfcollect == null) { + return; + } String reportName; try { reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip"; @@ -401,6 +404,9 @@ public final class ProfcollectForwardingService extends SystemService { final int traceDuration = 5000; final String traceTag = "camera"; BackgroundThread.get().getThreadHandler().post(() -> { + if (mIProfcollect == null) { + return; + } try { mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider", traceDuration); diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp index a738acb299c1..598e27372075 100644 --- a/services/tests/PackageManagerServiceTests/server/Android.bp +++ b/services/tests/PackageManagerServiceTests/server/Android.bp @@ -63,7 +63,7 @@ android_test { libs: [ "android.hardware.power-V1-java", "android.hardware.tv.cec-V1.0-java", - "android.hardware.vibrator-V2-java", + "android.hardware.vibrator-V3-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java index 26f6e91d29c8..df09b046ddd2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java @@ -46,6 +46,7 @@ public final class BrightnessEventTest { mBrightnessEvent.setPhysicalDisplayId("987654321"); mBrightnessEvent.setPhysicalDisplayName("display_name"); mBrightnessEvent.setDisplayState(Display.STATE_ON); + mBrightnessEvent.setDisplayStateReason(Display.STATE_REASON_DEFAULT_POLICY); mBrightnessEvent.setDisplayPolicy(POLICY_BRIGHT); mBrightnessEvent.setLux(100.0f); mBrightnessEvent.setPercent(46.5f); @@ -82,9 +83,9 @@ public final class BrightnessEventTest { String actualString = mBrightnessEvent.toString(false); String expectedString = "BrightnessEvent: brt=0.6 (46.5%), nits= 893.8, lux=100.0, reason=doze [ " - + "low_pwr ], strat=strategy_name, state=ON, policy=BRIGHT, flags=, " - + "initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, preLux=150.0, " - + "wasShortTermModelActive=true, autoBrightness=true (idle), " + + "low_pwr ], strat=strategy_name, state=ON, stateReason=DEFAULT_POLICY, " + + "policy=BRIGHT, flags=, initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, " + + "preLux=150.0, wasShortTermModelActive=true, autoBrightness=true (idle), " + "unclampedBrt=0.65, hbmMax=0.62, hbmMode=off, thrmMax=0.65, " + "rbcStrength=-1, powerFactor=0.2, physDisp=display_name(987654321), " + "logicalId=1"; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index adcbf5c9d059..194bf4ba73f3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -442,20 +442,24 @@ public class ApplicationExitInfoTest { IMPORTANCE_FOREGROUND_SERVICE, // importance null); // description - // Case 4: Create a process from another package with kill from lmkd + /* + * Case 4: Create a process from another package with kill from lmkd + * We expect LMKD's reported RSS to be the process' last seen RSS. + */ final int app2UidUser2 = 1010234; final int app2PidUser2 = 12348; final long app2Pss1 = 54321; final long app2Rss1 = 65432; + final long lmkd_reported_rss = 43215; final String app2ProcessName = "com.android.test.stub2:process"; final String app2PackageName = "com.android.test.stub2"; sleep(1); final long now4 = System.currentTimeMillis(); - doReturn(new Pair<Long, Object>(now4, Integer.valueOf(0))) + doReturn(null) .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) .remove(anyInt(), anyInt()); - doReturn(new Pair<Long, Object>(now4, null)) + doReturn(new Pair<Long, Object>(now4, Long.valueOf(lmkd_reported_rss))) .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) .remove(anyInt(), anyInt()); @@ -490,7 +494,7 @@ public class ApplicationExitInfoTest { null, // subReason 0, // status app2Pss1, // pss - app2Rss1, // rss + lmkd_reported_rss, // rss IMPORTANCE_CACHED, // importance null); // description @@ -499,6 +503,11 @@ public class ApplicationExitInfoTest { mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, 0, list); assertEquals(1, list.size()); + info = list.get(0); + + // Verify the AppExitInfo has the LMKD reported RSS + assertEquals(lmkd_reported_rss, info.getRss()); + // Case 5: App native crash final int app3UidUser2 = 1010345; final int app3PidUser2 = 12349; @@ -599,7 +608,7 @@ public class ApplicationExitInfoTest { null, // subReason 0, // status app2Pss1, // pss - app2Rss1, // rss + lmkd_reported_rss, // rss IMPORTANCE_CACHED, // importance null); // description diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index b9e99dd2e1e4..a888dadff5c6 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -94,7 +94,7 @@ android_test { libs: [ "android.hardware.power-V1-java", "android.hardware.tv.cec-V1.0-java", - "android.hardware.vibrator-V2-java", + "android.hardware.vibrator-V3-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp index da21cd3cf919..757bcd8e2193 100644 --- a/services/tests/vibrator/Android.bp +++ b/services/tests/vibrator/Android.bp @@ -16,7 +16,7 @@ android_test { ], libs: [ - "android.hardware.vibrator-V2-java", + "android.hardware.vibrator-V3-java", "android.test.mock", "android.test.base", "android.test.runner", @@ -36,7 +36,6 @@ android_test { "platform-test-annotations", "service-permission.stubs.system_server", "services.core", - "flag-junit", ], jni_libs: ["libdexmakerjvmtiagent"], platform_apis: true, diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java index 3013ed025bd9..59d557777f3b 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManagerInternal; import android.hardware.vibrator.IVibrator; import android.os.CombinedVibration; import android.os.Handler; +import android.os.PersistableBundle; import android.os.VibrationEffect; import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; @@ -32,6 +33,7 @@ import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; @@ -103,6 +105,17 @@ public class DeviceAdapterTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void testVendorEffect_returnsOriginalSegment() { + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putInt("key", 1); + VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData); + + assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_ID, effect)).isEqualTo(effect); + assertThat(mAdapter.adaptToVibrator(PWLE_VIBRATOR_ID, effect)).isEqualTo(effect); + } + + @Test public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() { VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList( // Step(amplitude, frequencyHz, duration) diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java index b2644350dfdd..9ebeaa8eb3fd 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java @@ -37,6 +37,7 @@ import android.content.ContextWrapper; import android.content.pm.PackageManagerInternal; import android.os.ExternalVibrationScale; import android.os.Handler; +import android.os.PersistableBundle; import android.os.PowerManagerInternal; import android.os.UserHandle; import android.os.VibrationAttributes; @@ -232,6 +233,34 @@ public class VibrationScalerTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() { + setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putString("key", "value"); + VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData); + + VibrationEffect.VendorEffect scaled = + (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION); + assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); + + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + VIBRATION_INTENSITY_MEDIUM); + scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION); + assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM); + + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION); + assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT); + + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION); + // Vibration setting being bypassed will use default setting. + assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT); + } + + @Test public void scale_withOneShotAndWaveform_resolvesAmplitude() { // No scale, default amplitude still resolved setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW); @@ -365,6 +394,30 @@ public class VibrationScalerTest { assertTrue(scaled.getAmplitude() > 0.5); } + @Test + @RequiresFlagsEnabled({ + android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED, + android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS, + }) + public void scale_adaptiveHapticsOnVendorEffect_setsLinearScaleParameter() { + setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH); + + mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f); + + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putInt("key", 1); + VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData); + + VibrationEffect.VendorEffect scaled = + (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE); + assertEquals(scaled.getLinearScale(), 0.5f); + + mVibrationScaler.removeAdaptiveHapticsScale(USAGE_RINGTONE); + + scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE); + assertEquals(scaled.getLinearScale(), 1.0f); + } + private void setDefaultIntensity(@VibrationAttributes.Usage int usage, @Vibrator.VibrationIntensity int intensity) { when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java index d7004e72bc52..3bd56deb32f4 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java @@ -48,6 +48,7 @@ import android.hardware.vibrator.IVibratorManager; import android.os.CombinedVibration; import android.os.Handler; import android.os.IBinder; +import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.SystemClock; @@ -560,8 +561,37 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread(() -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), + /* immediate= */ false)); + cancellingThread.start(); + + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); + } + + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_singleVibratorVendorEffectCancel_cancelsVibrationImmediately() + throws Exception { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + // Set long vendor effect duration to check it gets cancelled quickly. + mVibratorProviders.get(VIBRATOR_ID).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS); + + VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData()); + long vibrationId = startThreadAndDispatcher(effect); + + assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(), + TEST_TIMEOUT_MILLIS)); + assertTrue(mThread.isRunningVibrationId(vibrationId)); + + // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should + // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. + Thread cancellingThread = + new Thread(() -> mVibrationConductor.notifyCancelled( + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ false)); cancellingThread.start(); @@ -588,8 +618,7 @@ public class VibrationThreadTest { // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread(() -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); @@ -654,6 +683,27 @@ public class VibrationThreadTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_singleVibratorVendorEffect_runsVibration() { + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + + VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData()); + long vibrationId = startThreadAndDispatcher(effect); + waitForCompletion(); + + verify(mManagerHooks).noteVibratorOn(eq(UID), + eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS)); + verify(mManagerHooks).noteVibratorOff(eq(UID)); + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId)) + .containsExactly(effect) + .inOrder(); + } + + @Test public void vibrate_singleVibratorComposed_runsVibration() { FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); @@ -1437,16 +1487,48 @@ public class VibrationThreadTest { .combine(); long vibrationId = startThreadAndDispatcher(effect); - assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), - TEST_TIMEOUT_MILLIS)); + assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)); assertTrue(mThread.isRunningVibrationId(vibrationId)); // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. Thread cancellingThread = new Thread( () -> mVibrationConductor.notifyCancelled( - new Vibration.EndInfo( - Vibration.Status.CANCELLED_BY_SCREEN_OFF), + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), + /* immediate= */ false)); + cancellingThread.start(); + + waitForCompletion(/* timeout= */ 50); + cancellingThread.join(); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); + } + + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_multipleVendorEffectCancel_cancelsVibrationImmediately() throws Exception { + mockVibrators(1, 2); + mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + mVibratorProviders.get(1).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS); + mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + mVibratorProviders.get(2).setVendorEffectDuration(10 * TEST_TIMEOUT_MILLIS); + + CombinedVibration effect = CombinedVibration.startParallel() + .addVibrator(1, VibrationEffect.createVendorEffect(createTestVendorData())) + .addVibrator(2, VibrationEffect.createVendorEffect(createTestVendorData())) + .combine(); + long vibrationId = startThreadAndDispatcher(effect); + + assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS)); + assertTrue(mThread.isRunningVibrationId(vibrationId)); + + // Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should + // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately. + Thread cancellingThread = new Thread( + () -> mVibrationConductor.notifyCancelled( + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false)); cancellingThread.start(); @@ -1614,6 +1696,25 @@ public class VibrationThreadTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_vendorEffectWithRampDown_doesNotAddRampDown() { + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + + VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData()); + long vibrationId = startThreadAndDispatcher(effect); + waitForCompletion(); + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + + assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId)) + .containsExactly(effect) + .inOrder(); + assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty(); + } + + @Test public void vibrate_composedWithRampDown_doesNotAddRampDown() { when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, @@ -1831,6 +1932,16 @@ public class VibrationThreadTest { return array; } + private static PersistableBundle createTestVendorData() { + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putInt("id", 1); + vendorData.putDouble("scale", 0.5); + vendorData.putBoolean("loop", false); + vendorData.putLongArray("amplitudes", new long[] { 0, 255, 128 }); + vendorData.putString("label", "vibration"); + return vendorData; + } + private VibrationEffectSegment expectedOneShot(long millis) { return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequencyHz= */ 0, (int) millis); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 5ae5677b9b53..1f4a469bdd5a 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.vibrator; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -67,6 +69,7 @@ import android.os.IBinder; import android.os.IExternalVibrationController; import android.os.IVibratorStateListener; import android.os.Looper; +import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; @@ -1573,6 +1576,50 @@ public class VibratorManagerServiceTest { } @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception { + // Deny permission to vibrate with vendor effects + denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK); + VibratorManagerService service = createSystemReadyService(); + + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putString("key", "value"); + VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData); + VibrationEffect tickEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK); + + vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS); + vibrateAndWaitUntilFinished(service, tickEffect, RINGTONE_ATTRS); + + // No vendor effect played, but predefined TICK plays successfully. + assertThat(fakeVibrator.getAllVendorEffects()).isEmpty(); + assertThat(fakeVibrator.getAllEffectSegments()).hasSize(1); + assertThat(fakeVibrator.getAllEffectSegments().get(0)).isInstanceOf(PrebakedSegment.class); + } + + @Test + @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS) + public void vibrate_vendorEffectsWithPermission_successful() throws Exception { + // Deny permission to vibrate with vendor effects + grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS); + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + VibratorManagerService service = createSystemReadyService(); + + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putString("key", "value"); + VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData); + + vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS); + + assertThat(fakeVibrator.getAllVendorEffects()).containsExactly(vendorEffect); + } + + @Test public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception { int defaultNotificationIntensity = mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION); @@ -1714,6 +1761,39 @@ public class VibratorManagerServiceTest { } @Test + @RequiresFlagsEnabled({ + android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED, + android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS, + }) + public void vibrate_withIntensitySettingsAndAdaptiveHaptics_appliesSettingsToVendorEffects() + throws Exception { + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_LOW); + + mockVibrators(1); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); + fakeVibrator.setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS); + VibratorManagerService service = createSystemReadyService(); + + SparseArray<Float> vibrationScales = new SparseArray<>(); + vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); + + mVibratorControlService.setVibrationParams( + VibrationParamGenerator.generateVibrationParams(vibrationScales), + mFakeVibratorController); + + PersistableBundle vendorData = new PersistableBundle(); + vendorData.putString("key", "value"); + VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData); + vibrateAndWaitUntilFinished(service, vendorEffect, NOTIFICATION_ATTRS); + + assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1); + VibrationEffect.VendorEffect scaled = fakeVibrator.getAllVendorEffects().get(0); + assertThat(scaled.getEffectStrength()).isEqualTo(VibrationEffect.EFFECT_STRENGTH_STRONG); + assertThat(scaled.getLinearScale()).isEqualTo(0.4f); + } + + @Test public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception { mockVibrators(1, 2); VibratorManagerService service = createSystemReadyService(); @@ -2729,7 +2809,9 @@ public class VibratorManagerServiceTest { CombinedVibration effect, VibrationAttributes attrs) { HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect, attrs, "some reason", service); - mPendingVibrations.add(vib); + if (vib != null) { + mPendingVibrations.add(vib); + } return vib; } diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java index 2ddb47b832ef..96c3e97bc819 100644 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -17,8 +17,11 @@ package com.android.server.vibrator; import android.annotation.Nullable; +import android.hardware.vibrator.IVibrator; import android.os.Handler; import android.os.Looper; +import android.os.Parcel; +import android.os.PersistableBundle; import android.os.VibrationEffect; import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; @@ -45,6 +48,7 @@ public final class FakeVibratorControllerProvider { private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>(); private final Map<Long, List<VibrationEffectSegment>> mEffectSegments = new TreeMap<>(); + private final Map<Long, List<VibrationEffect.VendorEffect>> mVendorEffects = new TreeMap<>(); private final Map<Long, List<Integer>> mBraking = new HashMap<>(); private final List<Float> mAmplitudes = new ArrayList<>(); private final List<Boolean> mExternalControlStates = new ArrayList<>(); @@ -69,11 +73,16 @@ public final class FakeVibratorControllerProvider { private float mFrequencyResolution = Float.NaN; private float mQFactor = Float.NaN; private float[] mMaxAmplitudes; + private long mVendorEffectDuration = EFFECT_DURATION; void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) { mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment); } + void recordVendorEffect(long vibrationId, VibrationEffect.VendorEffect vendorEffect) { + mVendorEffects.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(vendorEffect); + } + void recordBraking(long vibrationId, int braking) { mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking); } @@ -130,6 +139,21 @@ public final class FakeVibratorControllerProvider { } @Override + public long performVendorEffect(Parcel vendorData, long strength, float scale, + long vibrationId) { + if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) { + return 0; + } + PersistableBundle bundle = PersistableBundle.CREATOR.createFromParcel(vendorData); + recordVendorEffect(vibrationId, + new VibrationEffect.VendorEffect(bundle, (int) strength, scale)); + applyLatency(mOnLatency); + scheduleListener(mVendorEffectDuration, vibrationId); + // HAL has unknown duration for vendor effects. + return Long.MAX_VALUE; + } + + @Override public long compose(PrimitiveSegment[] primitives, long vibrationId) { if (mSupportedPrimitives == null) { return 0; @@ -328,6 +352,11 @@ public final class FakeVibratorControllerProvider { mMaxAmplitudes = maxAmplitudes; } + /** Set the duration of vendor effects in fake vibrator hardware. */ + public void setVendorEffectDuration(long durationMs) { + mVendorEffectDuration = durationMs; + } + /** * Return the amplitudes set by this controller, including zeroes for each time the vibrator was * turned off. @@ -366,6 +395,29 @@ public final class FakeVibratorControllerProvider { } return result; } + + /** Return list of {@link VibrationEffect.VendorEffect} played by this controller, in order. */ + public List<VibrationEffect.VendorEffect> getVendorEffects(long vibrationId) { + if (mVendorEffects.containsKey(vibrationId)) { + return new ArrayList<>(mVendorEffects.get(vibrationId)); + } else { + return new ArrayList<>(); + } + } + + /** + * Returns a list of all vibrations' effect segments, for external-use where vibration IDs + * aren't exposed. + */ + public List<VibrationEffect.VendorEffect> getAllVendorEffects() { + // Returns segments in order of vibrationId, which increases over time. TreeMap gives order. + ArrayList<VibrationEffect.VendorEffect> result = new ArrayList<>(); + for (List<VibrationEffect.VendorEffect> subList : mVendorEffects.values()) { + result.addAll(subList); + } + return result; + } + /** Return list of states set for external control to the fake vibrator hardware. */ public List<Boolean> getExternalControlStates() { return mExternalControlStates; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 421bbae6fb14..faaa80f0c3e5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -523,9 +523,8 @@ public class ActivityRecordTests extends WindowTestsBase { assertEquals(newConfig.uiMode, activityConfig.uiMode); // The configuration change is still sent to the activity, even if it doesn't relaunch. - final ActivityConfigurationChangeItem expected = - ActivityConfigurationChangeItem.obtain(activity.token, activityConfig, - activity.getActivityWindowInfo()); + final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem( + activity.token, activityConfig, activity.getActivityWindowInfo()); verify(mClientLifecycleManager).scheduleTransactionItem( eq(activity.app.getThread()), eq(expected)); } @@ -596,9 +595,8 @@ public class ActivityRecordTests extends WindowTestsBase { final Configuration currentConfig = activity.getConfiguration(); assertEquals(expectedOrientation, currentConfig.orientation); - final ActivityConfigurationChangeItem expected = - ActivityConfigurationChangeItem.obtain(activity.token, currentConfig, - activity.getActivityWindowInfo()); + final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem( + activity.token, currentConfig, activity.getActivityWindowInfo()); verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected); verify(displayRotation).onSetRequestedOrientation(); } @@ -817,9 +815,8 @@ public class ActivityRecordTests extends WindowTestsBase { activity.ensureActivityConfiguration(true /* ignoreVisibility */); - final ActivityConfigurationChangeItem expected = - ActivityConfigurationChangeItem.obtain(activity.token, - activity.getConfiguration(), activity.getActivityWindowInfo()); + final ActivityConfigurationChangeItem expected = new ActivityConfigurationChangeItem( + activity.token, activity.getConfiguration(), activity.getActivityWindowInfo()); verify(mClientLifecycleManager).scheduleTransactionItem( activity.app.getThread(), expected); } finally { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java index 6ad1044d2012..14fbbe4cf2ef 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java @@ -191,9 +191,9 @@ public class ActivityRefresherTests extends WindowTestsBase { verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(), times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true); - final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token, - cycleThroughStop ? ON_STOP : ON_PAUSE); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token, + final RefreshCallbackItem refreshCallbackItem = + new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java index 867f01fd4699..11f7560a1409 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -190,6 +193,27 @@ class AppCompatActivityRobot { doReturn(embedded).when(mActivityStack.top()).isEmbedded(); } + void setTopActivityInMultiWindowMode(boolean multiWindowMode) { + doReturn(multiWindowMode).when(mActivityStack.top()).inMultiWindowMode(); + if (multiWindowMode) { + doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivityStack.top()).getWindowingMode(); + } + } + + void setTopActivityInPinnedWindowingMode(boolean pinnedWindowingMode) { + doReturn(pinnedWindowingMode).when(mActivityStack.top()).inPinnedWindowingMode(); + if (pinnedWindowingMode) { + doReturn(WINDOWING_MODE_PINNED).when(mActivityStack.top()).getWindowingMode(); + } + } + + void setTopActivityInFreeformWindowingMode(boolean freeformWindowingMode) { + doReturn(freeformWindowingMode).when(mActivityStack.top()).inFreeformWindowingMode(); + if (freeformWindowingMode) { + doReturn(WINDOWING_MODE_FREEFORM).when(mActivityStack.top()).getWindowingMode(); + } + } + void destroyTopActivity() { mActivityStack.top().removeImmediately(); } @@ -401,9 +425,11 @@ class AppCompatActivityRobot { private void pushActivity(@NonNull ActivityRecord activity) { mActivityStack.push(activity); spyOn(activity); + // TODO (b/351763164): Use these spyOn calls only when necessary. spyOn(activity.mAppCompatController.getTransparentPolicy()); spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides()); spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy()); + spyOn(activity.mAppCompatController.getAppCompatFocusOverrides()); spyOn(activity.mLetterboxUiController); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java index 0a1b16bfc3e9..00a87719eaac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java @@ -66,4 +66,8 @@ class AppCompatConfigurationRobot { doReturn(enabled).when(mAppCompatConfiguration) .isCameraCompatSplitScreenAspectRatioEnabled(); } + + void enableCompatFakeFocus(boolean enabled) { + doReturn(enabled).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java new file mode 100644 index 000000000000..27c5e4ebb397 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2024 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.server.wm; + +import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; +import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import android.compat.testing.PlatformCompatChangeRule; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.testng.Assert; + +import java.util.function.Consumer; + +/** + * Test class for {@link AppCompatFocusOverrides}. + * <p> + * Build/Install/Run: + * atest WmTests:AppCompatFocusOverridesTest + */ +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatFocusOverridesTest extends WindowTestsBase { + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_overrideEnabled_inMultiWindow_returnsTrue() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_overrideEnabled_noMultiWindowMode_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ false); + }); + + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_overrideEnabled_pinnedWindowMode_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInPinnedWindowingMode(/* multiWindowMode */ true); + }); + + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_overrideEnabled_freeformMode_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInFreeformWindowingMode(/* freeformWindowingMode */ true); + }); + + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideOn_fakeFocusDisabled() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true); + }); + } + + @Test + public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ false); + }); + } + + @Test + public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.setTopActivityInMultiWindowMode(/* multiWindowMode */ true); + }); + robot.checkShouldSendFakeFocusOnTopActivity(/* expected */ true); + }); + } + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer<FocusOverridesRobotTest> consumer) { + spyOn(mWm.mAppCompatConfiguration); + final FocusOverridesRobotTest robot = new FocusOverridesRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class FocusOverridesRobotTest extends AppCompatRobotBase { + + FocusOverridesRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + } + + void checkShouldSendFakeFocusOnTopActivity(boolean expected) { + Assert.assertEquals(activity().top().mAppCompatController.getAppCompatFocusOverrides() + .shouldSendFakeFocus(), expected); + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java new file mode 100644 index 000000000000..439c6337d905 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatSizeCompatTests.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2024 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.server.wm; + +import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; +import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import android.compat.testing.PlatformCompatChangeRule; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; +import androidx.test.filters.MediumTest; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; + +/** + * Tests for App Compat specific code about sizes. + * + * Build/Install/Run: + * atest WmTests:AppCompatSizeCompatTests + */ +@MediumTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatSizeCompatTests extends WindowTestsBase { + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusEnabledUnsetProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ true); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusEnabledTrueProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ true); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusEnabledFalseProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusDisabledUnsetProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusDisabledTrueProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().enable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ true); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusDisabledFalseProp() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ true); + robot.prop().disable(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + @Test + @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) + public void testShouldSendFakeFocus_compatFakeFocusEnabledFeatureDisabled() { + runTestScenario((robot) -> { + robot.conf().enableCompatFakeFocus(/* enabled */ false); + robot.activity().createActivityWithComponent(); + + robot.putTopActivityInMultiWindowMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInPinnedWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + + robot.putTopActivityInFreeformWindowingMode(); + robot.checkShouldSendCompatFakeFocus(/* expected */ false); + }); + } + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer<SizeCompatRobotTest> consumer) { + spyOn(mWm.mAppCompatConfiguration); + final SizeCompatRobotTest robot = new SizeCompatRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class SizeCompatRobotTest extends AppCompatRobotBase { + + SizeCompatRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + } + + void checkShouldSendCompatFakeFocus(boolean expected) { + Assert.assertEquals(expected, activity().top().shouldSendCompatFakeFocus()); + } + + void putTopActivityInMultiWindowMode() { + applyOnActivity((a) -> { + a.setTopActivityInMultiWindowMode(true); + a.setTopActivityInFreeformWindowingMode(false); + a.setTopActivityInPinnedWindowingMode(false); + }); + } + + void putTopActivityInPinnedWindowingMode() { + applyOnActivity((a) -> { + a.setTopActivityInMultiWindowMode(false); + a.setTopActivityInPinnedWindowingMode(true); + a.setTopActivityInFreeformWindowingMode(false); + }); + } + + void putTopActivityInFreeformWindowingMode() { + applyOnActivity((a) -> { + a.setTopActivityInMultiWindowMode(false); + a.setTopActivityInPinnedWindowingMode(false); + a.setTopActivityInFreeformWindowingMode(true); + }); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java index eaa164127551..f2592d2361f5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java @@ -307,9 +307,9 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(), times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true); - final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token, - cycleThroughStop ? ON_STOP : ON_PAUSE); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token, + final RefreshCallbackItem refreshCallbackItem = + new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java index 08f1dff7e1c0..771e290f60fd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -32,11 +32,13 @@ import static org.mockito.Mockito.when; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.view.SurfaceControl; import android.view.SurfaceSession; import com.android.server.testutils.StubTransaction; import com.android.server.wm.utils.MockAnimationAdapter; +import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Test; @@ -142,11 +144,12 @@ public class DimmerTests extends WindowTestsBase { } @Test + @RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY) public void testUpdateDimsAppliesCrop() { mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, 1, 1); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); int width = 100; int height = 300; @@ -163,7 +166,7 @@ public class DimmerTests extends WindowTestsBase { final int blur = 50; mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, alpha, blur); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); assertNotNull("Dimmer should have created a surface", dimLayer); @@ -184,12 +187,12 @@ public class DimmerTests extends WindowTestsBase { final int blur = 50; // Dim once mDimmer.adjustAppearance(mChild, alpha, blur); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); // Reset, and don't dim mDimmer.resetDimStates(); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); verify(mTransaction).show(dimLayer); verify(mTransaction).remove(dimLayer); @@ -203,24 +206,25 @@ public class DimmerTests extends WindowTestsBase { final int blur = 20; // Dim once mDimmer.adjustAppearance(mChild, alpha, blur); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); // Reset and dim again mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, alpha, blur); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); verify(mTransaction).show(dimLayer); verify(mTransaction, never()).remove(dimLayer); } @Test + @RequiresFlagsDisabled(Flags.FLAG_USE_TASKS_DIM_ONLY) public void testDimUpdateWhileDimming() { mHost.addChild(mChild, 0); final float alpha = 0.8f; mDimmer.adjustAppearance(mChild, alpha, 20); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); final Rect bounds = mDimmer.getDimBounds(); SurfaceControl dimLayer = mDimmer.getDimLayer(); @@ -240,7 +244,7 @@ public class DimmerTests extends WindowTestsBase { public void testRemoveDimImmediately() { mHost.addChild(mChild, 0); mDimmer.adjustAppearance(mChild, 1, 2); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); verify(mTransaction, times(1)).show(dimLayer); @@ -265,18 +269,18 @@ public class DimmerTests extends WindowTestsBase { mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0.1f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0.2f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0.3f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); verify(mTransaction).setAlpha(dimLayer, 0.2f); @@ -296,18 +300,18 @@ public class DimmerTests extends WindowTestsBase { mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0.2f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0.1f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); mDimmer.adjustAppearance(mChild, 0f, 0); - mDimmer.adjustRelativeLayer(mChild, -1); + mDimmer.adjustPosition(mChild, mChild, -1); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); @@ -326,13 +330,13 @@ public class DimmerTests extends WindowTestsBase { mHost.addChild(second, 1); mDimmer.adjustAppearance(first, 0.5f, 0); - mDimmer.adjustRelativeLayer(first, -1); + mDimmer.adjustPosition(mChild, first, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.updateDims(mTransaction); mDimmer.resetDimStates(); mDimmer.adjustAppearance(second, 0.9f, 0); - mDimmer.adjustRelativeLayer(second, -1); + mDimmer.adjustPosition(mChild, second, -1); mDimmer.updateDims(mTransaction); verify(sTestAnimation, times(2)).startAnimation( @@ -354,10 +358,10 @@ public class DimmerTests extends WindowTestsBase { mHost.addChild(second, 1); mDimmer.adjustAppearance(first, 0.5f, 0); - mDimmer.adjustRelativeLayer(first, -1); + mDimmer.adjustPosition(mChild, first, -1); SurfaceControl dimLayer = mDimmer.getDimLayer(); mDimmer.adjustAppearance(second, 0.9f, 0); - mDimmer.adjustRelativeLayer(second, -1); + mDimmer.adjustPosition(mChild, second, -1); mDimmer.updateDims(mTransaction); verify(sTestAnimation, times(1)).startAnimation( diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index e9fcc4048fbc..2dea6ba2b8aa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -613,9 +613,9 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { verify(mActivity.mAppCompatController.getAppCompatCameraOverrides(), times(refreshRequested ? 1 : 0)).setIsRefreshRequested(true); - final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token, - cycleThroughStop ? ON_STOP : ON_PAUSE); - final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token, + final RefreshCallbackItem refreshCallbackItem = + new RefreshCallbackItem(mActivity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); + final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(mActivity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 44c7057b2294..a0a29048fe26 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -18,14 +18,12 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP; import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; -import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS; import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; -import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; @@ -362,72 +360,6 @@ public class LetterboxUiControllerTest extends WindowTestsBase { } @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) - public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - - mController = new LetterboxUiController(mWm, mActivity); - - assertTrue(mController.shouldSendFakeFocus()); - } - - @Test - @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) - public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - - mController = new LetterboxUiController(mWm, mActivity); - - assertFalse(mController.shouldSendFakeFocus()); - } - - @Test - @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) - public void testIsCompatFakeFocusEnabled_propertyDisabledAndOverrideEnabled_fakeFocusDisabled() - throws Exception { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false); - - mController = new LetterboxUiController(mWm, mActivity); - - assertFalse(mController.shouldSendFakeFocus()); - } - - @Test - @DisableCompatChanges({OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS}) - public void testIsCompatFakeFocusEnabled_propertyEnabled_noOverride_fakeFocusEnabled() - throws Exception { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true); - - mController = new LetterboxUiController(mWm, mActivity); - - assertTrue(mController.shouldSendFakeFocus()); - } - - @Test - public void testIsCompatFakeFocusEnabled_propertyDisabled_fakeFocusDisabled() - throws Exception { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ false); - - mController = new LetterboxUiController(mWm, mActivity); - - assertFalse(mController.shouldSendFakeFocus()); - } - - @Test - public void testIsCompatFakeFocusEnabled_propertyEnabled_fakeFocusEnabled() - throws Exception { - doReturn(true).when(mAppCompatConfiguration).isCompatFakeFocusEnabled(); - mockThatProperty(PROPERTY_COMPAT_ENABLE_FAKE_FOCUS, /* value */ true); - - mController = new LetterboxUiController(mWm, mActivity); - - assertTrue(mController.shouldSendFakeFocus()); - } - - @Test @EnableCompatChanges({FORCE_RESIZE_APP}) public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() { mController = new LetterboxUiController(mWm, mActivity); diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java index 280fe4c30739..34f7ebb0f7dd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java @@ -66,6 +66,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -131,7 +132,7 @@ public class ScreenshotTests { assertTrue("Failed to wait for transaction to get committed", countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS)); assertTrue("Failed to wait for stable geometry", - waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS)); + waitForStableWindowGeometry(Duration.ofSeconds(WAIT_TIME_S))); ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC) .setCaptureSecureLayers(true) @@ -212,7 +213,7 @@ public class ScreenshotTests { assertTrue("Failed to wait for transaction to get committed", countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS)); assertTrue("Failed to wait for stable geometry", - waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS)); + waitForStableWindowGeometry(Duration.ofSeconds(WAIT_TIME_S))); ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1]; Bitmap screenshot = null; 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 8981f715cf4b..72747c974694 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -19,7 +19,6 @@ package com.android.server.wm; 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; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; @@ -4809,52 +4808,6 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(newDensity, mActivity.getConfiguration().densityDpi); } - @Test - public void testShouldSendFakeFocus_compatFakeFocusEnabled() { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setOnTop(true) - // Set the component to be that of the test class in order to enable compat changes - .setComponent(ComponentName.createRelative(mContext, - com.android.server.wm.SizeCompatTests.class.getName())) - .build(); - final Task task = activity.getTask(); - spyOn(activity.mLetterboxUiController); - doReturn(true).when(activity.mLetterboxUiController).shouldSendFakeFocus(); - - task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - assertTrue(activity.shouldSendCompatFakeFocus()); - - task.setWindowingMode(WINDOWING_MODE_PINNED); - assertFalse(activity.shouldSendCompatFakeFocus()); - - task.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertFalse(activity.shouldSendCompatFakeFocus()); - } - - @Test - public void testShouldSendFakeFocus_compatFakeFocusDisabled() { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setOnTop(true) - // Set the component to be that of the test class in order to enable compat changes - .setComponent(ComponentName.createRelative(mContext, - com.android.server.wm.SizeCompatTests.class.getName())) - .build(); - final Task task = activity.getTask(); - spyOn(activity.mLetterboxUiController); - doReturn(false).when(activity.mLetterboxUiController).shouldSendFakeFocus(); - - task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - assertFalse(activity.shouldSendCompatFakeFocus()); - - task.setWindowingMode(WINDOWING_MODE_PINNED); - assertFalse(activity.shouldSendCompatFakeFocus()); - - task.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertFalse(activity.shouldSendCompatFakeFocus()); - } - private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) { spyOn(mActivity.mLetterboxUiController); doReturn(thinLetterboxAllowed).when(mActivity.mLetterboxUiController) diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java index f1d84cfc636d..529e9b77b4e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java @@ -49,6 +49,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -144,7 +145,7 @@ public class TrustedOverlayTests { } } return false; - }, TIMEOUT_S, TimeUnit.SECONDS); + }, Duration.ofSeconds(TIMEOUT_S)); assertAndDumpWindowState(TAG, "Failed to find window or was not marked trusted", foundTrusted[0]); @@ -209,7 +210,7 @@ public class TrustedOverlayTests { } } return foundTrusted[0] && foundTrusted[1]; - }, TIMEOUT_S, TimeUnit.SECONDS); + }, Duration.ofSeconds(TIMEOUT_S)); if (!foundTrusted[0] || !foundTrusted[1]) { CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName()); diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java index eda78cb40c5d..381e9e46af92 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java @@ -20,7 +20,7 @@ import static com.android.server.wm.utils.DesktopModeFlagsUtil.DESKTOP_WINDOWING import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_OFF; import static com.android.server.wm.utils.DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_ON; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE; -import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY; +import static com.android.window.flags.Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS; import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION; import static com.google.common.truth.Truth.assertThat; @@ -188,145 +188,145 @@ public class DesktopModeFlagsUtilTest extends WindowTestsBase { @Test @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY}) + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS}) public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() { setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()); // For unset overrides, follow flag - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE}) - @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) public void isEnabled_dwFlagOn_overrideUnset_featureFlagOff_returnsFalse() { setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()); // For unset overrides, follow flag - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test @EnableFlags({ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) public void isEnabled_dwFlagOn_overrideOn_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_ON.getSetting()); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE}) - @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) public void isEnabled_dwFlagOn_overrideOn_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_ON.getSetting()); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test @EnableFlags({ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsFalse() { setOverride(OVERRIDE_OFF.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE}) - @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + @DisableFlags(FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS) public void isEnabled_dwFlagOn_overrideOff_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_OFF.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test @EnableFlags({ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void isEnabled_dwFlagOff_overrideUnset_featureFlagOn_returnsTrue() { setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()); // For unset overrides, follow flag - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags({ FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) public void isEnabled_dwFlagOff_overrideUnset_featureFlagOff_returnsFalse() { setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()); // For unset overrides, follow flag - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } @Test @EnableFlags({ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void isEnabled_dwFlagOff_overrideOn_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_ON.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags({ FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnTrue() { setOverride(OVERRIDE_ON.getSetting()); // Follow override if they exist, and is not equal to default toggle state (dw flag) - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags({ FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) public void isEnabled_dwFlagOff_overrideOff_featureFlagOn_returnsTrue() { setOverride(OVERRIDE_OFF.getSetting()); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isTrue(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue(); } @Test @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION) @DisableFlags({ FLAG_ENABLE_DESKTOP_WINDOWING_MODE, - FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY + FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS }) public void isEnabled_dwFlagOff_overrideOff_featureFlagOff_returnsFalse() { setOverride(OVERRIDE_OFF.getSetting()); // When toggle override matches its default state (dw flag), don't override flags - assertThat(DesktopModeFlagsUtil.WALLPAPER_ACTIVITY.isEnabled(mContext)).isFalse(); + assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse(); } private void setOverride(Integer setting) { diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 8d1fc508ffe7..d32cedb24a36 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -40,7 +40,7 @@ import androidx.test.uiautomator.Until import com.android.cts.input.DebugInputRule import com.android.cts.input.UinputTouchScreen -import java.util.concurrent.TimeUnit +import java.time.Duration import org.junit.After import org.junit.Assert.assertEquals @@ -193,6 +193,6 @@ class AnrTest { val flags = " -W -n " val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity" instrumentation.uiAutomation.executeShellCommand(startCmd) - waitForStableWindowGeometry(5L, TimeUnit.SECONDS) + waitForStableWindowGeometry(Duration.ofSeconds(5)) } } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 2df941834063..45bf8e38c5ce 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -690,7 +690,9 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, resource_format = item_iter->second.format; } - if (!ParseItem(parser, out_resource, resource_format)) { + // Don't bother parsing the item if it is behind a disabled flag + if (out_resource->flag_status != FlagStatus::Disabled && + !ParseItem(parser, out_resource, resource_format)) { return false; } return true; diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index b59b16574c42..2e6ad13d99de 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -69,8 +69,13 @@ class ResourceParserTest : public ::testing::Test { return TestParse(str, ConfigDescription{}); } - ::testing::AssertionResult TestParse(StringPiece str, const ConfigDescription& config) { - ResourceParserOptions parserOptions; + ::testing::AssertionResult TestParse(StringPiece str, ResourceParserOptions parserOptions) { + return TestParse(str, ConfigDescription{}, parserOptions); + } + + ::testing::AssertionResult TestParse( + StringPiece str, const ConfigDescription& config, + ResourceParserOptions parserOptions = ResourceParserOptions()) { ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config, parserOptions); @@ -242,6 +247,19 @@ TEST_F(ResourceParserTest, ParseStringTranslatableAttribute) { EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)")); } +TEST_F(ResourceParserTest, ParseStringBehindDisabledFlag) { + FeatureFlagProperties flag_properties(true, false); + ResourceParserOptions options; + options.feature_flag_values = {{"falseFlag", flag_properties}}; + ASSERT_TRUE(TestParse( + R"(<string name="foo" android:featureFlag="falseFlag" + xmlns:android="http://schemas.android.com/apk/res/android">foo</string>)", + options)); + + String* str = test::GetValue<String>(&table_, "string/foo"); + ASSERT_THAT(str, IsNull()); +} + TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) { std::string input = R"( <string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java index 7806061f00bb..b4a7663a1839 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/AppInfoTest.java @@ -86,11 +86,12 @@ public class AppInfoTest { @Test public void testAllFieldsValidV1() throws Exception { System.out.println("starting testAllFieldsValidV1."); - new AppInfoFactory() - .createFromOdElement( - TestUtils.getElementFromResource( - Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)), - 1L); + var unused = + new AppInfoFactory() + .createFromOdElement( + TestUtils.getElementFromResource( + Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)), + 1L); } /** Test for unrecognized field v1. */ @@ -133,7 +134,7 @@ public class AppInfoTest { TestUtils.getElementFromResource( Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_V1_FILE_NAME)); TestUtils.removeOdChildEleWithName(appInfoEle, optField); - new AppInfoFactory().createFromOdElement(appInfoEle, 1L); + var unused = new AppInfoFactory().createFromOdElement(appInfoEle, 1L); } } @@ -202,7 +203,7 @@ public class AppInfoTest { Paths.get(APP_INFO_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); ele.removeAttribute(optField); AppInfo appInfo = new AppInfoFactory().createFromHrElement(ele, DEFAULT_VERSION); - appInfo.toOdDomElement(TestUtils.document()); + var unused = appInfo.toOdDomElement(TestUtils.document()); } for (String optField : OPTIONAL_FIELD_NAMES_OD) { @@ -211,7 +212,7 @@ public class AppInfoTest { Paths.get(APP_INFO_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); TestUtils.removeOdChildEleWithName(ele, optField); AppInfo appInfo = new AppInfoFactory().createFromOdElement(ele, DEFAULT_VERSION); - appInfo.toHrDomElement(TestUtils.document()); + var unused = appInfo.toHrDomElement(TestUtils.document()); } } diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java index a4472b1b78e5..2746800532ab 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DeveloperInfoTest.java @@ -99,7 +99,7 @@ public class DeveloperInfoTest { developerInfoEle.removeAttribute(optField); DeveloperInfo developerInfo = new DeveloperInfoFactory().createFromHrElement(developerInfoEle); - developerInfo.toOdDomElement(TestUtils.document()); + var unused = developerInfo.toOdDomElement(TestUtils.document()); } for (String optField : OPTIONAL_FIELD_NAMES_OD) { @@ -109,7 +109,7 @@ public class DeveloperInfoTest { TestUtils.removeOdChildEleWithName(developerInfoEle, optField); DeveloperInfo developerInfo = new DeveloperInfoFactory().createFromOdElement(developerInfoEle); - developerInfo.toHrDomElement(TestUtils.document()); + var unused = developerInfo.toHrDomElement(TestUtils.document()); } } diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java index 9d197a2cf7f5..27f8720868dc 100644 --- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java +++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SecurityLabelsTest.java @@ -64,7 +64,7 @@ public class SecurityLabelsTest { Paths.get(SECURITY_LABELS_HR_PATH, ALL_FIELDS_VALID_FILE_NAME)); ele.removeAttribute(optField); SecurityLabels securityLabels = new SecurityLabelsFactory().createFromHrElement(ele); - securityLabels.toOdDomElement(TestUtils.document()); + var unused = securityLabels.toOdDomElement(TestUtils.document()); } for (String optField : OPTIONAL_FIELD_NAMES_OD) { var ele = @@ -72,7 +72,7 @@ public class SecurityLabelsTest { Paths.get(SECURITY_LABELS_OD_PATH, ALL_FIELDS_VALID_FILE_NAME)); TestUtils.removeOdChildEleWithName(ele, optField); SecurityLabels securityLabels = new SecurityLabelsFactory().createFromOdElement(ele); - securityLabels.toHrDomElement(TestUtils.document()); + var unused = securityLabels.toHrDomElement(TestUtils.document()); } } |