diff options
1356 files changed, 23178 insertions, 9311 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 1ae9ada41338..c7750123aef0 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -21,6 +21,7 @@ aconfig_declarations_group { java_aconfig_libraries: [ // !!! KEEP THIS LIST ALPHABETICAL !!! "aconfig_mediacodec_flags_java_lib", + "aconfig_trade_in_mode_flags_java_lib", "android-sdk-flags-java", "android.adaptiveauth.flags-aconfig-java", "android.app.appfunctions.flags-aconfig-java", @@ -123,7 +124,7 @@ aconfig_declarations_group { filegroup { name: "framework-minus-apex-aconfig-srcjars", - srcs: [ + device_common_srcs: [ ":framework-minus-apex-aconfig-declarations{.srcjars}", ], } @@ -1559,6 +1560,10 @@ java_aconfig_library { name: "android.crashrecovery.flags-aconfig-java", aconfig_declarations: "android.crashrecovery.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], + apex_available: [ + "//apex_available:platform", + "com.android.crashrecovery", + ], } java_aconfig_library { diff --git a/Android.bp b/Android.bp index 252aeef079d2..bfe749a7b49b 100644 --- a/Android.bp +++ b/Android.bp @@ -61,7 +61,7 @@ license { filegroup { name: "framework-non-updatable-sources", - srcs: [ + device_common_srcs: [ // Java/AIDL sources under frameworks/base ":framework-annotations", ":framework-blobstore-sources", @@ -109,7 +109,7 @@ filegroup { ":android.hardware.radio.voice-V3-java-source", ":android.hardware.security.keymint-V3-java-source", ":android.hardware.security.secureclock-V1-java-source", - ":android.hardware.thermal-V2-java-source", + ":android.hardware.thermal-V3-java-source", ":android.hardware.tv.tuner-V3-java-source", ":android.security.apc-java-source", ":android.security.authorization-java-source", diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp index 96b9d6af5f31..a95e1175fb0c 100644 --- a/apct-tests/perftests/core/apps/reources_manager/Android.bp +++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp @@ -27,7 +27,7 @@ android_test_helper_app { static_libs: ["androidx.appcompat_appcompat"], } -genrule { +java_genrule { name: "LargeResourcesUncompressed", srcs: [":LargeResourcesCompressed"], out: ["LargeResourcesUncompressed.apk"], diff --git a/apct-tests/perftests/tracing/Android.bp b/apct-tests/perftests/tracing/Android.bp index 08e365be514a..8814216644d7 100644 --- a/apct-tests/perftests/tracing/Android.bp +++ b/apct-tests/perftests/tracing/Android.bp @@ -22,6 +22,7 @@ android_test { "apct-perftests-utils", "collector-device-lib", "platform-test-annotations", + "perfetto_trace_java_protos", ], test_suites: [ "device-tests", diff --git a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java index f4759b8bd35c..7ef8c53d1d62 100644 --- a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java +++ b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java @@ -17,10 +17,12 @@ package com.android.internal.protolog; import android.app.Activity; import android.os.Bundle; +import android.os.ServiceManager.ServiceNotFoundException; import android.perftests.utils.Stats; import androidx.test.InstrumentationRegistry; +import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogLevel; @@ -31,6 +33,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import perfetto.protos.ProtologCommon; + import java.util.ArrayList; import java.util.Collection; @@ -65,24 +69,48 @@ public class ProtoLogPerfTest { }; } + private IProtoLog mProcessedProtoLogger; + private static final String MOCK_TEST_FILE_PATH = "mock/file/path"; + private static final perfetto.protos.Protolog.ProtoLogViewerConfig VIEWER_CONFIG = + perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() + .addGroups( + perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TestProtoLogGroup.TEST_GROUP.toString()) + .setTag(TestProtoLogGroup.TEST_GROUP.getTag()) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(123) + .setMessage("My Test Debug Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:123") + ).build(); + @BeforeClass public static void init() { ProtoLog.init(TestProtoLogGroup.values()); } @Before - public void setUp() { + public void setUp() throws ServiceNotFoundException { TestProtoLogGroup.TEST_GROUP.setLogToProto(mLogToProto); TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat); + + mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl( + MOCK_TEST_FILE_PATH, + () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()), + () -> {}, + TestProtoLogGroup.values() + ); } @Test public void log_Processed_NoArgs() { - final var protoLog = ProtoLog.getSingleInstance(); final var perfMonitor = new PerfMonitor(); while (perfMonitor.keepRunning()) { - protoLog.log( + mProcessedProtoLogger.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0, (Object[]) null); } @@ -90,11 +118,10 @@ public class ProtoLogPerfTest { @Test public void log_Processed_WithArgs() { - final var protoLog = ProtoLog.getSingleInstance(); final var perfMonitor = new PerfMonitor(); while (perfMonitor.keepRunning()) { - protoLog.log( + mProcessedProtoLogger.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123, 0b1110101001010100, new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); diff --git a/api/Android.bp b/api/Android.bp index 3f2316f005bd..3c92cb26b0fe 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -124,7 +124,7 @@ combined_apis { }), } -genrule { +java_genrule { name: "frameworks-base-api-current-compat", srcs: [ ":android.api.public.latest", @@ -140,7 +140,7 @@ genrule { "$(location :frameworks-base-api-current.txt)", } -genrule { +java_genrule { name: "frameworks-base-api-system-current-compat", srcs: [ ":android.api.public.latest", @@ -160,7 +160,7 @@ genrule { "$(location :frameworks-base-api-system-current.txt)", } -genrule { +java_genrule { name: "frameworks-base-api-module-lib-current-compat", srcs: [ ":android.api.public.latest", @@ -184,7 +184,7 @@ genrule { "$(location :frameworks-base-api-module-lib-current.txt)", } -genrule { +java_genrule { name: "frameworks-base-api-current.srcjar", tools: ["merge_zips"], out: ["current.srcjar"], @@ -209,7 +209,7 @@ genrule_defaults { "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out", } -genrule { +java_genrule { name: "sdk-annotations.zip", defaults: ["sdk-annotations-defaults"], srcs: [ @@ -218,7 +218,7 @@ genrule { ], } -genrule { +java_genrule { name: "sdk-annotations-system.zip", defaults: ["sdk-annotations-defaults"], srcs: [ @@ -227,7 +227,7 @@ genrule { ], } -genrule { +java_genrule { name: "sdk-annotations-module-lib.zip", defaults: ["sdk-annotations-defaults"], srcs: [ @@ -236,7 +236,7 @@ genrule { ], } -genrule { +java_genrule { name: "sdk-annotations-system-server.zip", defaults: ["sdk-annotations-defaults"], srcs: [ @@ -245,7 +245,7 @@ genrule { ], } -genrule { +java_genrule { name: "combined-removed-dex", visibility: [ "//frameworks/base/boot", @@ -460,7 +460,7 @@ genrule_defaults { tools: ["extract-flagged-apis"], } -genrule { +java_genrule { name: "flag-api-mapping-PublicApi", defaults: ["flag-api-mapping-generation-defaults"], srcs: [":frameworks-base-api-current.txt"], @@ -470,7 +470,7 @@ genrule { }, } -genrule { +java_genrule { name: "flag-api-mapping-SystemApi", defaults: ["flag-api-mapping-generation-defaults"], srcs: [":frameworks-base-api-system-current.txt"], @@ -480,7 +480,7 @@ genrule { }, } -genrule { +java_genrule { name: "flag-api-mapping-ModuleLibApi", defaults: ["flag-api-mapping-generation-defaults"], srcs: [":frameworks-base-api-module-lib-current.txt"], @@ -490,7 +490,7 @@ genrule { }, } -genrule { +java_genrule { name: "flag-api-mapping-SystemServerApi", defaults: ["flag-api-mapping-generation-defaults"], srcs: [":frameworks-base-api-system-server-current.txt"], diff --git a/api/api.go b/api/api.go index 1bbf3709480a..29083dfc8968 100644 --- a/api/api.go +++ b/api/api.go @@ -20,7 +20,6 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/genrule" "android/soong/java" ) @@ -138,9 +137,10 @@ type libraryProps struct { } type fgProps struct { - Name *string - Srcs proptools.Configurable[[]string] - Visibility []string + Name *string + Srcs proptools.Configurable[[]string] + Device_common_srcs proptools.Configurable[[]string] + Visibility []string } type defaultsProps struct { @@ -201,7 +201,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubs } } props.Visibility = []string{"//visibility:public"} - ctx.CreateModule(genrule.GenRuleFactory, &props) + ctx.CreateModule(java.GenRuleFactory, &props) } func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) { @@ -230,7 +230,7 @@ func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, sys } { props := fgProps{} props.Name = proptools.StringPtr(i.name) - props.Srcs = createSrcs(i.modules, i.tag) + props.Device_common_srcs = createSrcs(i.modules, i.tag) ctx.CreateModule(android.FileGroupFactory, &props) } } @@ -429,7 +429,7 @@ func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContex func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) { props := fgProps{} props.Name = proptools.StringPtr("all-modules-public-stubs-source") - props.Srcs = createSrcs(modules, "{.public.stubs.source}") + props.Device_common_srcs = createSrcs(modules, "{.public.stubs.source}") props.Visibility = []string{"//frameworks/base"} ctx.CreateModule(android.FileGroupFactory, &props) } diff --git a/api/api_test.go b/api/api_test.go index fb26f821eec1..166f053978f2 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -253,7 +253,7 @@ func TestCombinedApisDefaults(t *testing.T) { `) subModuleDependsOnSelectAppendedModule := java.CheckModuleHasDependency(t, - result.TestContext, "foo-current.txt", "", "framework-foo") + result.TestContext, "foo-current.txt", "android_common", "framework-foo") android.AssertBoolEquals(t, "Submodule expected to depend on the select-appended module", true, subModuleDependsOnSelectAppendedModule) } diff --git a/boot/Android.bp b/boot/Android.bp index f60bb9e7149a..6eead42a4d30 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -234,7 +234,7 @@ custom_platform_bootclasspath { ], } -genrule { // This module exists to make the srcjar output available to Make. +java_genrule { // This module exists to make the srcjar output available to Make. name: "platform-bootclasspath.srcjar", srcs: [":platform-bootclasspath{.srcjar}"], out: ["platform-bootclasspath.srcjar"], diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt index e3b187e0a66d..11ca1dcc181e 100644 --- a/boot/boot-image-profile-extra.txt +++ b/boot/boot-image-profile-extra.txt @@ -19,3 +19,7 @@ # methods are latency sensitive is difficult. For example, this method is executed # in the system server, not on the UI thread of an app. HSPLandroid/graphics/Color;->luminance()F + +# For now, compile all methods in MessageQueue to avoid performance cliffs for +# flagged/evolving hot code paths. See: b/338098106 +HSPLandroid/os/MessageQueue;->* diff --git a/core/api/Android.bp b/core/api/Android.bp index 77594b758d19..06eea52881ea 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -100,50 +100,50 @@ filegroup { // Exportable stub artifacts filegroup { name: "non-updatable-exportable-current.txt", - srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"], + device_common_srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"], } filegroup { name: "non-updatable-exportable-removed.txt", - srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], + device_common_srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], } filegroup { name: "non-updatable-exportable-system-current.txt", - srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"], + device_common_srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"], } filegroup { name: "non-updatable-exportable-system-removed.txt", - srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], + device_common_srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], } filegroup { name: "non-updatable-exportable-module-lib-current.txt", - srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"], + device_common_srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"], } filegroup { name: "non-updatable-exportable-module-lib-removed.txt", - srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], + device_common_srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], } filegroup { name: "non-updatable-exportable-test-current.txt", - srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"], + device_common_srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"], } filegroup { name: "non-updatable-exportable-test-removed.txt", - srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], + device_common_srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"], } filegroup { name: "non-updatable-exportable-system-server-current.txt", - srcs: [":services-non-updatable-stubs{.exportable.api.txt}"], + device_common_srcs: [":services-non-updatable-stubs{.exportable.api.txt}"], } filegroup { name: "non-updatable-exportable-system-server-removed.txt", - srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"], + device_common_srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"], } diff --git a/core/api/current.txt b/core/api/current.txt index a8b9e331de2a..46a864e19865 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13373,6 +13373,11 @@ package android.content.pm { field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint"; field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt"; field public static final String FEATURE_WINDOW_MAGNIFICATION = "android.software.window_magnification"; + field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FEATURE_XR_API_OPENXR = "android.software.xr.api.openxr"; + field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FEATURE_XR_API_SPATIAL = "android.software.xr.api.spatial"; + field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FEATURE_XR_INPUT_CONTROLLER = "android.hardware.xr.input.controller"; + field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FEATURE_XR_INPUT_EYE_TRACKING = "android.hardware.xr.input.eye_tracking"; + field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FEATURE_XR_INPUT_HAND_TRACKING = "android.hardware.xr.input.hand_tracking"; field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2 field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1 field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4 @@ -16369,6 +16374,7 @@ package android.graphics { field public static final int UNKNOWN = 0; // 0x0 field public static final int Y8 = 538982489; // 0x20203859 field public static final int YCBCR_P010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c field public static final int YUV_420_888 = 35; // 0x23 field public static final int YUV_422_888 = 39; // 0x27 field public static final int YUV_444_888 = 40; // 0x28 @@ -18718,6 +18724,7 @@ package android.hardware { field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L field public static final int YCBCR_420_888 = 35; // 0x23 field public static final int YCBCR_P010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c } @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable { @@ -22985,6 +22992,7 @@ package android.media { field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888 field @Deprecated public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d field public static final int COLOR_FormatYUVP010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int COLOR_FormatYUVP210 = 60; // 0x3c field @Deprecated public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00 field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100 field public static final String FEATURE_AdaptivePlayback = "adaptive-playback"; @@ -32844,6 +32852,7 @@ package android.os { public static class Build.VERSION_CODES { ctor public Build.VERSION_CODES(); + field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int BAKLAVA = 10000; // 0x2710 field public static final int BASE = 1; // 0x1 field public static final int BASE_1_1 = 2; // 0x2 field public static final int CUPCAKE = 3; // 0x3 @@ -32883,6 +32892,7 @@ package android.os { } @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL { + field public static final int BAKLAVA = 1000000000; // 0x3b9aca00 field public static final int BASE = 100000; // 0x186a0 field public static final int BASE_1_1 = 200000; // 0x30d40 field public static final int CUPCAKE = 300000; // 0x493e0 @@ -37072,6 +37082,7 @@ package android.provider { @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount { method @FlaggedApi("android.provider.new_default_account_api_enabled") @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState getDefaultAccountForNewContacts(@NonNull android.content.ContentResolver); + field public static final String ACTION_MOVE_CONTACTS_TO_DEFAULT_ACCOUNT = "android.provider.action.MOVE_CONTACTS_TO_DEFAULT_ACCOUNT"; } @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState { @@ -52491,6 +52502,7 @@ package android.view { method public android.graphics.Canvas lockHardwareCanvas(); method public void readFromParcel(android.os.Parcel); method public void release(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public void setFrameRate(@NonNull android.view.Surface.FrameRateParams); method public void setFrameRate(@FloatRange(from=0.0) float, int, int); method public void setFrameRate(@FloatRange(from=0.0) float, int); method @Deprecated public void unlockCanvas(android.graphics.Canvas); @@ -52507,6 +52519,22 @@ package android.view { field public static final int ROTATION_90 = 1; // 0x1 } + @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static class Surface.FrameRateParams { + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public int getChangeFrameRateStrategy(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMaxRate(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMinRate(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getFixedSourceRate(); + field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final android.view.Surface.FrameRateParams IGNORE; + } + + @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final class Surface.FrameRateParams.Builder { + ctor public Surface.FrameRateParams.Builder(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams build(); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setChangeFrameRateStrategy(int); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setDesiredRateRange(@FloatRange(from=0.0) float, @FloatRange(from=0.0) float); + method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setFixedSourceRate(@FloatRange(from=0.0) float); + } + public static class Surface.OutOfResourcesException extends java.lang.RuntimeException { ctor public Surface.OutOfResourcesException(); ctor public Surface.OutOfResourcesException(String); @@ -61755,7 +61783,7 @@ package android.window { public final class BackEvent { ctor public BackEvent(float, float, float, int); ctor @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public BackEvent(float, float, float, int, long); - method @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public long getFrameTime(); + method @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public long getFrameTimeMillis(); method @FloatRange(from=0, to=1) public float getProgress(); method public int getSwipeEdge(); method public float getTouchX(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 207f4b57e8bf..389789b5fff4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -209,6 +209,8 @@ package android { field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY"; field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE"; + field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"; + field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"; field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY"; @@ -4637,7 +4639,7 @@ package android.content.pm.verify.pkg { @FlaggedApi("android.content.pm.verification_service") public final class VerificationSession implements android.os.Parcelable { method public int describeContents(); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long); + method public long extendTimeRemaining(long); method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredLibraries(); method @NonNull public android.os.PersistableBundle getExtensionParams(); method public int getId(); @@ -4645,12 +4647,12 @@ package android.content.pm.verify.pkg { method @NonNull public String getPackageName(); method @NonNull public android.content.pm.SigningInfo getSigningInfo(); method @NonNull public android.net.Uri getStagedPackageUri(); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime(); + method public long getTimeoutTime(); method public int getVerificationPolicy(); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int); - method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int); + method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus); + method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle); + method public void reportVerificationIncomplete(int); + method public boolean setVerificationPolicy(int); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR; field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1 @@ -6054,7 +6056,7 @@ package android.hardware.location { method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub(); method @IntRange(from=0, to=65535) public int getId(); method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage); - method @FlaggedApi("android.chre.flags.reliable_message") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendReliableMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendReliableMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage); } public class ContextHubClientCallback { @@ -6090,7 +6092,7 @@ package android.hardware.location { method public String getToolchain(); method public int getToolchainVersion(); method public String getVendor(); - method @FlaggedApi("android.chre.flags.reliable_message") public boolean supportsReliableMessages(); + method public boolean supportsReliableMessages(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR; } @@ -6174,7 +6176,7 @@ package android.hardware.location { field public static final int RESULT_FAILED_BAD_PARAMS = 2; // 0x2 field public static final int RESULT_FAILED_BUSY = 4; // 0x4 field public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; // 0x8 - field @FlaggedApi("android.chre.flags.reliable_message") public static final int RESULT_FAILED_NOT_SUPPORTED = 9; // 0x9 + field public static final int RESULT_FAILED_NOT_SUPPORTED = 9; // 0x9 field public static final int RESULT_FAILED_SERVICE_INTERNAL_FAILURE = 7; // 0x7 field public static final int RESULT_FAILED_TIMEOUT = 6; // 0x6 field public static final int RESULT_FAILED_UNINITIALIZED = 3; // 0x3 @@ -6184,7 +6186,7 @@ package android.hardware.location { field public static final int TYPE_ENABLE_NANOAPP = 2; // 0x2 field public static final int TYPE_LOAD_NANOAPP = 0; // 0x0 field public static final int TYPE_QUERY_NANOAPPS = 4; // 0x4 - field @FlaggedApi("android.chre.flags.reliable_message") public static final int TYPE_RELIABLE_MESSAGE = 5; // 0x5 + field public static final int TYPE_RELIABLE_MESSAGE = 5; // 0x5 field public static final int TYPE_UNLOAD_NANOAPP = 1; // 0x1 } @@ -6367,15 +6369,15 @@ package android.hardware.location { public final class NanoAppMessage implements android.os.Parcelable { method public static android.hardware.location.NanoAppMessage createMessageFromNanoApp(long, int, byte[], boolean); - method @FlaggedApi("android.chre.flags.reliable_message") @NonNull public static android.hardware.location.NanoAppMessage createMessageFromNanoApp(long, int, @NonNull byte[], boolean, boolean, int); + method @NonNull public static android.hardware.location.NanoAppMessage createMessageFromNanoApp(long, int, @NonNull byte[], boolean, boolean, int); method public static android.hardware.location.NanoAppMessage createMessageToNanoApp(long, int, byte[]); method public int describeContents(); method public byte[] getMessageBody(); - method @FlaggedApi("android.chre.flags.reliable_message") public int getMessageSequenceNumber(); + method public int getMessageSequenceNumber(); method public int getMessageType(); method public long getNanoAppId(); method public boolean isBroadcastMessage(); - method @FlaggedApi("android.chre.flags.reliable_message") public boolean isReliable(); + method public boolean isReliable(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppMessage> CREATOR; } @@ -6528,6 +6530,92 @@ package android.hardware.radio { field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR; } + @FlaggedApi("android.hardware.radio.hd_radio_emergency_alert_system") public final class RadioAlert implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.hardware.radio.RadioAlert.AlertInfo> getInfoList(); + method public int getMessageType(); + method public int getStatus(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CATEGORY_CBRNE = 10; // 0xa + field public static final int CATEGORY_ENV = 7; // 0x7 + field public static final int CATEGORY_FIRE = 5; // 0x5 + field public static final int CATEGORY_GEO = 0; // 0x0 + field public static final int CATEGORY_HEALTH = 6; // 0x6 + field public static final int CATEGORY_INFRA = 9; // 0x9 + field public static final int CATEGORY_MET = 1; // 0x1 + field public static final int CATEGORY_OTHER = 11; // 0xb + field public static final int CATEGORY_RESCUE = 4; // 0x4 + field public static final int CATEGORY_SAFETY = 2; // 0x2 + field public static final int CATEGORY_SECURITY = 3; // 0x3 + field public static final int CATEGORY_TRANSPORT = 8; // 0x8 + field public static final int CERTAINTY_LIKELY = 1; // 0x1 + field public static final int CERTAINTY_OBSERVED = 0; // 0x0 + field public static final int CERTAINTY_POSSIBLE = 2; // 0x2 + field public static final int CERTAINTY_UNKNOWN = 4; // 0x4 + field public static final int CERTAINTY_UNLIKELY = 3; // 0x3 + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert> CREATOR; + field public static final int MESSAGE_TYPE_ALERT = 0; // 0x0 + field public static final int MESSAGE_TYPE_CANCEL = 2; // 0x2 + field public static final int MESSAGE_TYPE_UPDATE = 1; // 0x1 + field public static final int SEVERITY_EXTREME = 0; // 0x0 + field public static final int SEVERITY_MINOR = 3; // 0x3 + field public static final int SEVERITY_MODERATE = 2; // 0x2 + field public static final int SEVERITY_SEVERE = 1; // 0x1 + field public static final int SEVERITY_UNKNOWN = 4; // 0x4 + field public static final int STATUS_ACTUAL = 0; // 0x0 + field public static final int STATUS_EXERCISE = 1; // 0x1 + field public static final int STATUS_TEST = 2; // 0x2 + field public static final int URGENCY_EXPECTED = 1; // 0x1 + field public static final int URGENCY_FUTURE = 2; // 0x2 + field public static final int URGENCY_IMMEDIATE = 0; // 0x0 + field public static final int URGENCY_PAST = 3; // 0x3 + field public static final int URGENCY_UNKNOWN = 4; // 0x4 + } + + public static final class RadioAlert.AlertArea implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Geocode> getGeocodes(); + method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Polygon> getPolygons(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.AlertArea> CREATOR; + } + + public static final class RadioAlert.AlertInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.hardware.radio.RadioAlert.AlertArea> getAreas(); + method @NonNull public int[] getCategories(); + method public int getCertainty(); + method @NonNull public String getDescription(); + method @Nullable public String getLanguage(); + method public int getSeverity(); + method public int getUrgency(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.AlertInfo> CREATOR; + } + + public static final class RadioAlert.Coordinate implements android.os.Parcelable { + method public int describeContents(); + method @FloatRange(from=-90.0, to=90.0) public double getLatitude(); + method @FloatRange(from=-180.0, to=180.0) public double getLongitude(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Coordinate> CREATOR; + } + + public static final class RadioAlert.Geocode implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getValue(); + method @NonNull public String getValueName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Geocode> CREATOR; + } + + public static final class RadioAlert.Polygon implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Coordinate> getCoordinates(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Polygon> CREATOR; + } + public class RadioManager { method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener); method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener); @@ -6663,6 +6751,7 @@ package android.hardware.radio { public static class RadioManager.ProgramInfo implements android.os.Parcelable { method public int describeContents(); + method @FlaggedApi("android.hardware.radio.hd_radio_emergency_alert_system") @Nullable public android.hardware.radio.RadioAlert getAlert(); method @Deprecated public int getChannel(); method @Nullable public android.hardware.radio.ProgramSelector.Identifier getLogicallyTunedTo(); method public android.hardware.radio.RadioMetadata getMetadata(); @@ -9429,6 +9518,7 @@ package android.media.tv.tuner.frontend { method public int getSignalStrength(); method public int getSnr(); method public int getSpectralInversion(); + method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExt getStandardExt(); method @NonNull public int[] getStreamIds(); method public int getSymbolRate(); method @IntRange(from=0, to=65535) public int getSystemId(); @@ -9483,6 +9573,7 @@ package android.media.tv.tuner.frontend { field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6 field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1 field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa + field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = 47; // 0x2f field public static final int FRONTEND_STATUS_TYPE_STREAM_IDS = 39; // 0x27 field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7 field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d @@ -9774,6 +9865,11 @@ package android.media.tv.tuner.frontend { method public default void onUnlocked(); } + @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExt { + method public int getDvbsStandardExt(); + method public int getDvbtStandardExt(); + } + } package android.media.voice { diff --git a/core/java/Android.bp b/core/java/Android.bp index d12e1bf77e17..9875efe04361 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -345,6 +345,13 @@ filegroup { } filegroup { + name: "service-crashrecovery-shared-srcs", + srcs: [ + "android/util/IndentingPrintWriter.java", + ], +} + +filegroup { name: "incremental_aidl", srcs: [ "android/os/incremental/IIncrementalServiceConnector.aidl", diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ed6b3d4a4632..52251745f758 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2681,7 +2681,10 @@ public final class ActivityThread extends ClientTransactionHandler handleUnstableProviderDied((IBinder)msg.obj, false); break; case REQUEST_ASSIST_CONTEXT_EXTRAS: + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "handleRequestAssistContextExtras"); handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case TRANSLUCENT_CONVERSION_COMPLETE: handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); @@ -7678,7 +7681,7 @@ public final class ActivityThread extends ClientTransactionHandler }); // Register callback to report native memory metrics post GC cleanup - if (Flags.reportPostgcMemoryMetrics() && + if (Flags.reportPostgcMemoryMetricsReadonly() && com.android.libcore.readonly.Flags.postCleanupApis()) { VMRuntime.addPostCleanupCallback(new Runnable() { @Override public void run() { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 2e3d22647a0f..56538d92baa0 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -8755,21 +8755,9 @@ public class AppOpsManager { * Do a quick check for whether an application might be able to perform an operation. * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String, * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual - * security checks, which also ensure that the given uid and package name are consistent. This - * function can just be used for a quick check to see if an operation has been disabled for the - * application, as an early reject of some work. This does not modify the time stamp or other - * data about the operation. - * - * <p>Important things this will not do (which you need to ultimate use - * {@link #noteOp(String, int, String, String, String)} or - * {@link #startOp(String, int, String, String, String)} to cover):</p> - * <ul> - * <li>Verifying the uid and package are consistent, so callers can't spoof - * their identity.</li> - * <li>Taking into account the current foreground/background state of the - * app; apps whose mode varies by this state will always be reported - * as {@link #MODE_ALLOWED}.</li> - * </ul> + * security checks. This function can just be used for a quick check to see if an operation has + * been disabled for the application, as an early reject of some work. This does not modify the + * time stamp or other data about the operation. * * @param op The operation to check. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index ffb920b907ab..f880901429f7 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -501,7 +501,7 @@ interface IActivityManager { in String shareDescription); void requestInteractiveBugReport(); - void requestBugReportWithExtraAttachment(in Uri extraAttachment); + void requestBugReportWithExtraAttachments(in List<Uri> extraAttachment); void requestFullBugReport(); void requestRemoteBugReport(long nonce); boolean launchBugReportHandlerApp(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 64aa705447aa..c179e38a953d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1870,7 +1870,7 @@ public class Notification implements Parcelable * You can test if a RemoteInput matches these constraints using * {@link RemoteInput#isDataOnly}. */ - private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; + static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; /** * No semantic action defined. @@ -2089,7 +2089,7 @@ public class Notification implements Parcelable * of non-textual RemoteInputs do not access these remote inputs. */ public RemoteInput[] getDataOnlyRemoteInputs() { - return getParcelableArrayFromBundle(mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); + return mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); } /** @@ -2330,8 +2330,8 @@ public class Notification implements Parcelable checkContextualActionNullFields(); ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>(); - RemoteInput[] previousDataInputs = getParcelableArrayFromBundle( - mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); + RemoteInput[] previousDataInputs = + mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); if (previousDataInputs != null) { for (RemoteInput input : previousDataInputs) { dataOnlyInputs.add(input); @@ -3091,7 +3091,8 @@ public class Notification implements Parcelable visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI))); } - ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); + ArrayList<Person> people = + extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); if (people != null && !people.isEmpty()) { for (Person p : people) { p.visitUris(visitor); @@ -3117,8 +3118,8 @@ public class Notification implements Parcelable person.visitUris(visitor); } - final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, - Parcelable.class); + final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, + Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { @@ -3126,8 +3127,8 @@ public class Notification implements Parcelable } } - final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, - Parcelable.class); + final Bundle[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, + Bundle.class); if (!ArrayUtils.isEmpty(historic)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(historic)) { @@ -3838,17 +3839,9 @@ public class Notification implements Parcelable */ private void fixDuplicateExtras() { if (extras != null) { - fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON); - } - } - - /** - * If we find an extra that's exactly the same as one of the "real" fields but refers to a - * separate object, replace it with the field's version to avoid holding duplicate copies. - */ - private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) { - if (original != null && extras.getParcelable(extraName, Parcelable.class) != null) { - extras.putParcelable(extraName, original); + if (mLargeIcon != null) { + extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); + } } } @@ -6622,8 +6615,8 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } - RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle( - mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); + RemoteInputHistoryItem[] replyText = mN.extras.getParcelableArray( + EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { @@ -8027,8 +8020,7 @@ public class Notification implements Parcelable */ public boolean hasImage() { if (isStyle(MessagingStyle.class) && extras != null) { - final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, - Parcelable.class); + final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message m : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { @@ -9348,10 +9340,10 @@ public class Notification implements Parcelable mUser = user; } mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE); - Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); + Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); mMessages = Message.getMessagesFromBundleArray(messages); Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, - Parcelable.class); + Bundle.class); mHistoricMessages = Message.getMessagesFromBundleArray(histMessages); mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION); mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); @@ -10218,8 +10210,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } - RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle( - mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem[] remoteInputHistory = mBuilder.mN.extras.getParcelableArray( + EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { @@ -13070,7 +13062,8 @@ public class Notification implements Parcelable public WearableExtender(Notification notif) { Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS); if (wearableBundle != null) { - List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS, android.app.Notification.Action.class); + List<Action> actions = wearableBundle.getParcelableArrayList( + KEY_ACTIONS, Notification.Action.class); if (actions != null) { mActions.addAll(actions); } @@ -13079,8 +13072,8 @@ public class Notification implements Parcelable mDisplayIntent = wearableBundle.getParcelable( KEY_DISPLAY_INTENT, PendingIntent.class); - Notification[] pages = getParcelableArrayFromBundle( - wearableBundle, KEY_PAGES, Notification.class); + Notification[] pages = + wearableBundle.getParcelableArray(KEY_PAGES, Notification.class); if (pages != null) { Collections.addAll(mPages, pages); } @@ -14015,7 +14008,7 @@ public class Notification implements Parcelable if (mParticipants != null && mParticipants.length > 1) { author = mParticipants[0]; } - Parcelable[] messages = new Parcelable[mMessages.length]; + Bundle[] messages = new Bundle[mMessages.length]; for (int i = 0; i < messages.length; i++) { Bundle m = new Bundle(); m.putString(KEY_TEXT, mMessages[i]); @@ -14037,8 +14030,7 @@ public class Notification implements Parcelable if (b == null) { return null; } - Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, - Parcelable.class); + Bundle[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, Bundle.class); String[] messages = null; if (parcelableMessages != null) { String[] tmp = new String[parcelableMessages.length]; @@ -14402,27 +14394,6 @@ public class Notification implements Parcelable } } - /** - * Get an array of Parcelable objects from a parcelable array bundle field. - * Update the bundle to have a typed array so fetches in the future don't need - * to do an array copy. - */ - @Nullable - private static <T extends Parcelable> T[] getParcelableArrayFromBundle( - Bundle bundle, String key, Class<T> itemClass) { - final Parcelable[] array = bundle.getParcelableArray(key, Parcelable.class); - final Class<?> arrayClass = Array.newInstance(itemClass, 0).getClass(); - if (arrayClass.isInstance(array) || array == null) { - return (T[]) array; - } - final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length); - for (int i = 0; i < array.length; i++) { - typedArray[i] = (T) array[i]; - } - bundle.putParcelableArray(key, typedArray); - return typedArray; - } - private static class BuilderRemoteViews extends RemoteViews { public BuilderRemoteViews(Parcel parcel) { super(parcel); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index acedef0c7788..bc9e709420f1 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -30,20 +30,23 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; -import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.ApplicationSharedMemory; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.FastPrintWriter; + +import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -207,19 +210,14 @@ public class PropertyInvalidatedCache<Query, Result> { }; /** - * Verify that the property name conforms to the standard. Log a warning if this is not true. - * Note that this is done once in the cache constructor; it does not have to be very fast. + * Verify that the property name conforms to the standard and throw if this is not true. Note + * that this is done only once for a given property name; it does not have to be very fast. */ - private void validateCacheKey(String name) { - if (Build.IS_USER) { - // Do not bother checking keys in user builds. The keys will have been tested in - // eng/userdebug builds already. - return; - } + private static void throwIfInvalidCacheKey(String name) { for (int i = 0; i < sValidKeyPrefix.length; i++) { if (name.startsWith(sValidKeyPrefix[i])) return; } - Log.w(TAG, "invalid cache name: " + name); + throw new IllegalArgumentException("invalid cache name: " + name); } /** @@ -238,7 +236,8 @@ public class PropertyInvalidatedCache<Query, Result> { * reserved values cause the cache to be skipped. */ // This is the initial value of all cache keys. It is changed when a cache is invalidated. - private static final int NONCE_UNSET = 0; + @VisibleForTesting + static final int NONCE_UNSET = 0; // This value is used in two ways. First, it is used internally to indicate that the cache is // disabled for the current query. Secondly, it is used to globally disable the cache across // the entire system. Once a cache is disabled, there is no way to enable it again. The @@ -689,6 +688,77 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Manage nonces that are stored in shared memory. + */ + private static final class NonceSharedMem extends NonceHandler { + // The shared memory. + private volatile NonceStore mStore; + + // The index of the nonce in shared memory. + private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX; + + // True if the string has been stored, ever. + private volatile boolean mRecorded = false; + + // A short name that is saved in shared memory. This is the portion of the property name + // that follows the prefix. + private final String mShortName; + + NonceSharedMem(@NonNull String name, @Nullable String prefix) { + super(name); + if ((prefix != null) && name.startsWith(prefix)) { + mShortName = name.substring(prefix.length()); + } else { + mShortName = name; + } + } + + // Fetch the nonce from shared memory. If the shared memory is not available, return + // UNSET. If the shared memory is available but the nonce name is not known (it may not + // have been invalidated by the server yet), return UNSET. + @Override + long getNonceInternal() { + if (mHandle == NonceStore.INVALID_NONCE_INDEX) { + if (mStore == null) { + mStore = NonceStore.getInstance(); + if (mStore == null) { + return NONCE_UNSET; + } + } + mHandle = mStore.getHandleForName(mShortName); + if (mHandle == NonceStore.INVALID_NONCE_INDEX) { + return NONCE_UNSET; + } + } + return mStore.getNonce(mHandle); + } + + // Set the nonce in shared mmory. If the shared memory is not available, throw an + // exception. Otherwise, if the nonce name has never been recorded, record it now and + // fetch the handle for the name. If the handle cannot be created, throw an exception. + @Override + void setNonceInternal(long value) { + if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) { + if (mStore == null) { + mStore = NonceStore.getInstance(); + if (mStore == null) { + throw new IllegalStateException("setNonce: shared memory not ready"); + } + } + // Always store the name before fetching the handle. storeName() is idempotent + // but does take a little time, so this code calls it just once. + mStore.storeName(mShortName); + mRecorded = true; + mHandle = mStore.getHandleForName(mShortName); + if (mHandle == NonceStore.INVALID_NONCE_INDEX) { + throw new IllegalStateException("setNonce: shared memory store failed"); + } + } + mStore.setNonce(mHandle, value); + } + } + + /** * SystemProperties and shared storage are protected and cannot be written by random * processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The * NonceLocal uses the mTestNonce in the superclass, regardless of test mode. @@ -716,6 +786,7 @@ public class PropertyInvalidatedCache<Query, Result> { * Complete key prefixes. */ private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + "."; + private static final String PREFIX_SYSTEM = CACHE_KEY_PREFIX + "." + MODULE_SYSTEM + "."; /** * A static list of nonce handlers, indexed by name. NonceHandlers can be safely shared by @@ -726,16 +797,32 @@ public class PropertyInvalidatedCache<Query, Result> { private static final ConcurrentHashMap<String, NonceHandler> sHandlers = new ConcurrentHashMap<>(); + // True if shared memory is flag-enabled, false otherwise. Read the flags exactly once. + private static final boolean sSharedMemoryAvailable = + com.android.internal.os.Flags.applicationSharedMemoryEnabled() + && android.app.Flags.picUsesSharedMemory(); + + // Return true if this cache can use shared memory for its nonce. Shared memory may be used + // if the module is the system. + private static boolean sharedMemoryOkay(@NonNull String name) { + return sSharedMemoryAvailable && name.startsWith(PREFIX_SYSTEM); + } + /** - * Return the proper nonce handler, based on the property name. + * Return the proper nonce handler, based on the property name. A handler is created if + * necessary. Before a handler is created, the name is checked, and an exception is thrown if + * the name is not valid. */ private static NonceHandler getNonceHandler(@NonNull String name) { NonceHandler h = sHandlers.get(name); if (h == null) { synchronized (sGlobalLock) { + throwIfInvalidCacheKey(name); h = sHandlers.get(name); if (h == null) { - if (name.startsWith(PREFIX_TEST)) { + if (sharedMemoryOkay(name)) { + h = new NonceSharedMem(name, PREFIX_SYSTEM); + } else if (name.startsWith(PREFIX_TEST)) { h = new NonceLocal(name); } else { h = new NonceSysprop(name); @@ -778,7 +865,6 @@ public class PropertyInvalidatedCache<Query, Result> { public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName, @NonNull String cacheName) { mPropertyName = propertyName; - validateCacheKey(mPropertyName); mCacheName = cacheName; mNonce = getNonceHandler(mPropertyName); mMaxEntries = maxEntries; @@ -803,7 +889,6 @@ public class PropertyInvalidatedCache<Query, Result> { public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api, @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) { mPropertyName = createPropertyName(module, api); - validateCacheKey(mPropertyName); mCacheName = cacheName; mNonce = getNonceHandler(mPropertyName); mMaxEntries = maxEntries; @@ -1494,6 +1579,7 @@ public class PropertyInvalidatedCache<Query, Result> { final static String NAME_LIKE = "-name-like="; final static String PROPERTY_CONTAINS = "-property-has="; final static String PROPERTY_LIKE = "-property-like="; + final static String BRIEF = "-brief"; /** * Return true if any argument is a detailed specification switch. @@ -1539,6 +1625,21 @@ public class PropertyInvalidatedCache<Query, Result> { return false; } + /** + * helper method to check if dump should be skipped due to zero values + * @param args takes command arguments to check if -brief is present + * @return True if dump should be skipped + */ + private boolean skipDump(String[] args) { + for (String a : args) { + if (a.equals(BRIEF)) { + return (mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED] + + mSkips[NONCE_BYPASS] + mHits + mMisses) == 0; + } + } + return false; + } + private void dumpContents(PrintWriter pw, boolean detailed, String[] args) { // If the user has requested specific caches and this is not one of them, return // immediately. @@ -1549,25 +1650,28 @@ public class PropertyInvalidatedCache<Query, Result> { NonceHandler.Stats stats = mNonce.getStats(); synchronized (mLock) { - pw.println(formatSimple(" Cache Name: %s", cacheName())); - pw.println(formatSimple(" Property: %s", mPropertyName)); - final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED] - + mSkips[NONCE_BYPASS]; - pw.println(formatSimple( - " Hits: %d, Misses: %d, Skips: %d, Clears: %d", - mHits, mMisses, skips, mClears)); - pw.println(formatSimple( - " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d", - mSkips[NONCE_CORKED], mSkips[NONCE_UNSET], - mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED])); - pw.println(formatSimple( - " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d", - mLastSeenNonce, stats.invalidated, stats.corkedInvalidates)); - pw.println(formatSimple( - " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", - mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); - pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true")); - pw.println(""); + if (!skipDump(args)) { + pw.println(formatSimple(" Cache Name: %s", cacheName())); + pw.println(formatSimple(" Property: %s", mPropertyName)); + final long skips = + mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED] + + mSkips[NONCE_BYPASS]; + pw.println(formatSimple( + " Hits: %d, Misses: %d, Skips: %d, Clears: %d", + mHits, mMisses, skips, mClears)); + pw.println(formatSimple( + " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d", + mSkips[NONCE_CORKED], mSkips[NONCE_UNSET], + mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED])); + pw.println(formatSimple( + " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d", + mLastSeenNonce, stats.invalidated, stats.corkedInvalidates)); + pw.println(formatSimple( + " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", + mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); + pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true")); + pw.println(""); + } // No specific cache was requested. This is the default, and no details // should be dumped. @@ -1605,6 +1709,14 @@ public class PropertyInvalidatedCache<Query, Result> { // then only that cache is reported. boolean detail = anyDetailed(args); + if (sSharedMemoryAvailable) { + pw.println(" SharedMemory: enabled"); + NonceStore.getInstance().dump(pw, " ", detail); + } else { + pw.println(" SharedMemory: disabled"); + } + pw.println(); + ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches(); for (int i = 0; i < activeCaches.size(); i++) { PropertyInvalidatedCache currentCache = activeCaches.get(i); @@ -1639,4 +1751,363 @@ public class PropertyInvalidatedCache<Query, Result> { Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances"); } } + + /** + * Nonces in shared memory are supported by a string block that acts as a table of contents + * for nonce names, and an array of nonce values. There are two key design principles with + * respect to nonce maps: + * + * 1. It is always okay if a nonce value cannot be determined. If the nonce is UNSET, the + * cache is bypassed, which is always functionally correct. Clients do not take extraordinary + * measures to be current with the nonce map. Clients must be current with the nonce itself; + * this is achieved through the shared memory. + * + * 2. Once a name is mapped to a nonce index, the mapping is fixed for the lifetime of the + * system. It is only necessary to distinguish between the unmapped and mapped states. Once + * a client has mapped a nonce, that mapping is known to be good for the lifetime of the + * system. + * @hide + */ + @VisibleForTesting + public static class NonceStore { + + // A lock for the store. + private final Object mLock = new Object(); + + // The native pointer. This is not owned by this class. It is owned by + // ApplicationSharedMemory, and it disappears when the owning instance is closed. + private final long mPtr; + + // True if the memory is immutable. + private final boolean mMutable; + + // The maximum length of a string in the string block. The maximum length must fit in a + // byte, but a smaller value has been chosen to limit memory use. Because strings are + // run-length encoded, a string consumes at most MAX_STRING_LENGTH+1 bytes in the string + // block. + private static final int MAX_STRING_LENGTH = 63; + + // The raw byte block. Strings are stored as run-length encoded byte arrays. The first + // byte is the length of the following string. It is an axiom of the system that the + // string block is initially all zeros and that it is write-once memory: new strings are + // appended to existing strings, so there is never a need to revisit strings that have + // already been pulled from the string block. + @GuardedBy("mLock") + private final byte[] mStringBlock; + + // The expected hash code of the string block. If the hash over the string block equals + // this value, then the string block is valid. Otherwise, the block is not valid and + // should be re-read. An invalid block generally means that a client has read the shared + // memory while the server was still writing it. + @GuardedBy("mLock") + private int mBlockHash = 0; + + // The number of nonces that the native layer can hold. This is maintained for debug and + // logging. + private final int mMaxNonce; + + /** @hide */ + @VisibleForTesting + public NonceStore(long ptr, boolean mutable) { + mPtr = ptr; + mMutable = mutable; + mStringBlock = new byte[nativeGetMaxByte(ptr)]; + mMaxNonce = nativeGetMaxNonce(ptr); + refreshStringBlockLocked(); + } + + // The static lock for singleton acquisition. + private static Object sLock = new Object(); + + // NonceStore is supposed to be a singleton. + private static NonceStore sInstance; + + // Return the singleton instance. + static NonceStore getInstance() { + synchronized (sLock) { + if (sInstance == null) { + try { + ApplicationSharedMemory shmem = ApplicationSharedMemory.getInstance(); + sInstance = (shmem == null) + ? null + : new NonceStore(shmem.getSystemNonceBlock(), + shmem.isMutable()); + } catch (IllegalStateException e) { + // ApplicationSharedMemory.getInstance() throws if the shared memory is + // not yet mapped. Swallow the exception and leave sInstance null. + } + } + return sInstance; + } + } + + // The index value of an unmapped name. + public static final int INVALID_NONCE_INDEX = -1; + + // The highest string index extracted from the string block. -1 means no strings have + // been seen. This is used to skip strings that have already been processed, when the + // string block is updated. + @GuardedBy("mLock") + private int mHighestIndex = -1; + + // The number bytes of the string block that has been used. This is a statistics. + @GuardedBy("mLock") + private int mStringBytes = 0; + + // The number of partial reads on the string block. This is a statistic. + @GuardedBy("mLock") + private int mPartialReads = 0; + + // The number of times the string block was updated. This is a statistic. + @GuardedBy("mLock") + private int mStringUpdated = 0; + + // Map a string to a native index. + @GuardedBy("mLock") + private final ArrayMap<String, Integer> mStringHandle = new ArrayMap<>(); + + // Update the string map from the current string block. The string block is not modified + // and the block hash is not checked. The function skips past strings that have already + // been read, and then processes any new strings. + @GuardedBy("mLock") + private void updateStringMapLocked() { + int index = 0; + int offset = 0; + while (offset < mStringBlock.length && mStringBlock[offset] != 0) { + if (index > mHighestIndex) { + // Only record the string if it has not been seen yet. + final String s = new String(mStringBlock, offset+1, mStringBlock[offset]); + mStringHandle.put(s, index); + mHighestIndex = index; + } + offset += mStringBlock[offset] + 1; + index++; + } + mStringBytes = offset; + } + + // Append a string to the string block and update the hash. This does not write the block + // to shared memory. + @GuardedBy("mLock") + private void appendStringToMapLocked(@NonNull String str) { + int offset = 0; + while (offset < mStringBlock.length && mStringBlock[offset] != 0) { + offset += mStringBlock[offset] + 1; + } + final byte[] strBytes = str.getBytes(); + + if (offset + strBytes.length >= mStringBlock.length) { + // Overflow. Do not add the string to the block; the string will remain undefined. + return; + } + + mStringBlock[offset] = (byte) strBytes.length; + offset++; + for (int i = 0; i < strBytes.length; i++, offset++) { + mStringBlock[offset] = strBytes[i]; + } + mBlockHash = Arrays.hashCode(mStringBlock); + } + + // Possibly update the string block. If the native shared memory has a new block hash, + // then read the new string block values from shared memory, as well as the new hash. + @GuardedBy("mLock") + private void refreshStringBlockLocked() { + if (mBlockHash == nativeGetByteBlockHash(mPtr)) { + // The fastest way to know that the shared memory string block has not changed. + return; + } + final int hash = nativeGetByteBlock(mPtr, mBlockHash, mStringBlock); + if (hash != Arrays.hashCode(mStringBlock)) { + // This is a partial read: ignore it. The next time someone needs this string + // the memory will be read again and should succeed. Set the local hash to + // zero to ensure that the next read attempt will actually read from shared + // memory. + mBlockHash = 0; + mPartialReads++; + return; + } + // The hash has changed. Update the strings from the byte block. + mStringUpdated++; + mBlockHash = hash; + updateStringMapLocked(); + } + + // Throw an exception if the string cannot be stored in the string block. + private static void throwIfBadString(@NonNull String s) { + if (s.length() == 0) { + throw new IllegalArgumentException("cannot store an empty string"); + } + if (s.length() > MAX_STRING_LENGTH) { + throw new IllegalArgumentException("cannot store a string longer than " + + MAX_STRING_LENGTH); + } + } + + // Throw an exception if the nonce handle is invalid. The handle is bad if it is out of + // range of allocated handles. Note that NONCE_HANDLE_INVALID will throw: this is + // important for setNonce(). + @GuardedBy("mLock") + private void throwIfBadHandle(int handle) { + if (handle < 0 || handle > mHighestIndex) { + throw new IllegalArgumentException("invalid nonce handle: " + handle); + } + } + + // Throw if the memory is immutable (the process does not have write permission). The + // exception mimics the permission-denied exception thrown when a process writes to an + // unauthorized system property. + private void throwIfImmutable() { + if (!mMutable) { + throw new RuntimeException("write permission denied"); + } + } + + // Add a string to the local copy of the block and write the block to shared memory. + // Return the index of the new string. If the string has already been recorded, the + // shared memory is not updated but the index of the existing string is returned. + public int storeName(@NonNull String str) { + synchronized (mLock) { + Integer handle = mStringHandle.get(str); + if (handle == null) { + throwIfImmutable(); + throwIfBadString(str); + appendStringToMapLocked(str); + nativeSetByteBlock(mPtr, mBlockHash, mStringBlock); + updateStringMapLocked(); + handle = mStringHandle.get(str); + } + return handle; + } + } + + // Retrieve the handle for a string. -1 is returned if the string is not found. + public int getHandleForName(@NonNull String str) { + synchronized (mLock) { + Integer handle = mStringHandle.get(str); + if (handle == null) { + refreshStringBlockLocked(); + handle = mStringHandle.get(str); + } + return (handle != null) ? handle : INVALID_NONCE_INDEX; + } + } + + // Thin wrapper around the native method. + public boolean setNonce(int handle, long value) { + synchronized (mLock) { + throwIfBadHandle(handle); + throwIfImmutable(); + return nativeSetNonce(mPtr, handle, value); + } + } + + public long getNonce(int handle) { + synchronized (mLock) { + throwIfBadHandle(handle); + return nativeGetNonce(mPtr, handle); + } + } + + /** + * Dump the nonce statistics + */ + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, boolean detailed) { + synchronized (mLock) { + pw.println(formatSimple( + "%sStringsMapped: %d, BytesUsed: %d", + prefix, mHighestIndex, mStringBytes)); + pw.println(formatSimple( + "%sPartialReads: %d, StringUpdates: %d", + prefix, mPartialReads, mStringUpdated)); + + if (detailed) { + for (String s: mStringHandle.keySet()) { + int h = mStringHandle.get(s); + pw.println(formatSimple( + "%sHandle:%d Name:%s", prefix, h, s)); + } + } + } + } + } + + /** + * Return the maximum number of nonces supported in the native layer. + * + * @param mPtr the pointer to the native shared memory. + * @return the number of nonces supported by the shared memory. + */ + private static native int nativeGetMaxNonce(long mPtr); + + /** + * Return the maximum number of string bytes supported in the native layer. + * + * @param mPtr the pointer to the native shared memory. + * @return the number of string bytes supported by the shared memory. + */ + private static native int nativeGetMaxByte(long mPtr); + + /** + * Write the byte block and set the hash into shared memory. The method is relatively + * forgiving, in that any non-null byte array will be stored without error. The number of + * bytes will the lesser of the length of the block parameter and the size of the native + * array. The native layer performs no checks on either byte block or the hash. + * + * @param mPtr the pointer to the native shared memory. + * @param hash a value to be stored in the native block hash. + * @param block the byte array to be store. + */ + @FastNative + private static native void nativeSetByteBlock(long mPtr, int hash, @NonNull byte[] block); + + /** + * Retrieve the string block into the array and return the hash value. If the incoming hash + * value is the same as the hash in shared memory, the native function returns immediately + * without touching the block parameter. Note that a zero hash value will always cause shared + * memory to be read. The number of bytes read is the lesser of the length of the block + * parameter and the size of the native array. + * + * @param mPtr the pointer to the native shared memory. + * @param hash a value to be compared against the hash in the native layer. + * @param block an array to receive the bytes from the native layer. + * @return the hash from the native layer. + */ + @FastNative + private static native int nativeGetByteBlock(long mPtr, int hash, @NonNull byte[] block); + + /** + * Retrieve just the byte block hash from the native layer. The function is CriticalNative + * and thus very fast. + * + * @param mPtr the pointer to the native shared memory. + * @return the current native hash value. + */ + @CriticalNative + private static native int nativeGetByteBlockHash(long mPtr); + + /** + * Set a nonce at the specified index. The index is checked against the size of the native + * nonce array and the function returns true if the index is valid, and false. The function + * is CriticalNative and thus very fast. + * + * @param mPtr the pointer to the native shared memory. + * @param index the index of the nonce to set. + * @param value the value to set for the nonce. + * @return true if the index is inside the nonce array and false otherwise. + */ + @CriticalNative + private static native boolean nativeSetNonce(long mPtr, int index, long value); + + /** + * Get the nonce from the specified index. The index is checked against the size of the + * native nonce array; the function returns the nonce value if the index is valid, and 0 + * otherwise. The function is CriticalNative and thus very fast. + * + * @param mPtr the pointer to the native shared memory. + * @param index the index of the nonce to retrieve. + * @return the value of the specified nonce, of 0 if the index is out of bounds. + */ + @CriticalNative + private static native long nativeGetNonce(long mPtr, int index); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index bd26db55052b..c6b8f3baecf6 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1750,10 +1750,13 @@ public final class SystemServiceRegistry { @Override public AdvancedProtectionManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder iBinder = ServiceManager.getServiceOrThrow( + IBinder iBinder = ServiceManager.getService( Context.ADVANCED_PROTECTION_SERVICE); IAdvancedProtectionService service = IAdvancedProtectionService.Stub.asInterface(iBinder); + if (service == null) { + return null; + } return new AdvancedProtectionManager(service); } }); diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java index dca433696fe7..a82c6ba7479c 100644 --- a/core/java/android/app/appfunctions/AppFunctionManager.java +++ b/core/java/android/app/appfunctions/AppFunctionManager.java @@ -47,7 +47,6 @@ import java.util.function.Consumer; * <p>App function is a specific piece of functionality that an app offers to the system. These * functionalities can be integrated into various system features. */ -// TODO(b/357551503): Implement get and set enabled app function APIs. @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) @SystemService(Context.APP_FUNCTION_SERVICE) public final class AppFunctionManager { @@ -111,17 +110,19 @@ public final class AppFunctionManager { * @param request the request to execute the app function * @param executor the executor to run the callback * @param cancellationSignal the cancellation signal to cancel the execution. - * @param callback the callback to receive the function execution result. if the calling app - * does not own the app function or does not have {@code + * @param callback the callback to receive the function execution result. + * <p>If the calling app does not own the app function or does not have {@code * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code * ExecuteAppFunctionResponse.RESULT_DENIED}. + * <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the + * function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution + * result will contain {@code ExecuteAppFunctionResponse.RESULT_DENIED} + * <p>If the function requested for execution is disabled, then the execution result will + * contain {@code ExecuteAppFunctionResponse.RESULT_DISABLED} + * <p>If the cancellation signal is issued, the operation is cancelled and no response is + * returned to the caller. */ - // TODO(b/357551503): Document the behavior when the cancellation signal is issued. - // TODO(b/360864791): Document that apps can opt-out from being executed by callers with - // EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out. - // TODO(b/357551503): Update documentation when get / set APIs are implemented that this will - // also return RESULT_DENIED if the app function is disabled. @RequiresPermission( anyOf = { Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java index fe7fd8837624..4c5e8c130a73 100644 --- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java +++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java @@ -50,12 +50,12 @@ public final class ExecuteAppFunctionRequest implements Parcelable { } }; - /** Returns the package name of the app that hosts the function. */ + /** Returns the package name of the app that hosts/owns the function. */ @NonNull private final String mTargetPackageName; /** - * Returns the unique string identifier of the app function to be executed. TODO(b/357551503): - * Document how callers can get the available function identifiers. + * The unique string identifier of the app function to be executed. This identifier is used to + * execute a specific app function. */ @NonNull private final String mFunctionIdentifier; @@ -69,8 +69,6 @@ public final class ExecuteAppFunctionRequest implements Parcelable { * * <p>The document may have missing parameters. Developers are advised to implement defensive * handling measures. - * - * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution */ @NonNull private final GenericDocumentWrapper mParameters; @@ -91,7 +89,19 @@ public final class ExecuteAppFunctionRequest implements Parcelable { return mTargetPackageName; } - /** Returns the unique string identifier of the app function to be executed. */ + /** + * Returns the unique string identifier of the app function to be executed. + * + * <p>When there is a package change or the device starts up, the metadata of available + * functions is indexed by AppSearch. AppSearch stores the indexed information as {@code + * AppFunctionStaticMetadata} document. + * + * <p>The ID can be obtained by querying the {@code AppFunctionStaticMetadata} documents from + * AppSearch. + * + * <p>If the {@code functionId} provided is invalid, the caller will get an invalid argument + * response. + */ @NonNull public String getFunctionIdentifier() { return mFunctionIdentifier; @@ -103,6 +113,12 @@ public final class ExecuteAppFunctionRequest implements Parcelable { * * <p>The bundle may have missing parameters. Developers are advised to implement defensive * handling measures. + * + * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be + * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This + * metadata will contain enough information for the caller to resolve the required parameters + * either using information from the metadata itself or using the AppFunction SDK for function + * callers. */ @NonNull public GenericDocument getParameters() { diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java index a879b1ba6b5c..c907ef114286 100644 --- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java +++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java @@ -79,14 +79,15 @@ public final class ExecuteAppFunctionResponse implements Parcelable { /** The caller does not have the permission to execute an app function. */ public static final int RESULT_DENIED = 1; - /** An unknown error occurred while processing the call in the AppFunctionService. */ - public static final int RESULT_APP_UNKNOWN_ERROR = 2; - /** - * An internal error occurred within AppFunctionManagerService. + * An unknown error occurred while processing the call in the AppFunctionService. * - * <p>This error may be considered similar to {@link IllegalStateException} + * <p>This error is thrown when the service is connected in the remote application but an + * unexpected error is thrown from the bound application. */ + public static final int RESULT_APP_UNKNOWN_ERROR = 2; + + /** An internal unexpected error coming from the system. */ public static final int RESULT_INTERNAL_ERROR = 3; /** diff --git a/core/java/android/app/metrics.aconfig b/core/java/android/app/metrics.aconfig index 488f1c71990b..55d9c2d6b347 100644 --- a/core/java/android/app/metrics.aconfig +++ b/core/java/android/app/metrics.aconfig @@ -8,3 +8,12 @@ flag { description: "Controls whether to report memory metrics post GC cleanup" bug: "331243037" } + +flag { + namespace: "system_performance" + name: "report_postgc_memory_metrics_readonly" + is_exported: false + description: "Controls whether to report memory metrics post GC cleanup (readonly)" + bug: "331243037" + is_fixed_read_only: true +} diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig new file mode 100644 index 000000000000..7c6989e4f3e9 --- /dev/null +++ b/core/java/android/app/performance.aconfig @@ -0,0 +1,11 @@ +package: "android.app" +container: "system" + +flag { + namespace: "system_performance" + name: "pic_uses_shared_memory" + is_exported: true + is_fixed_read_only: true + description: "PropertyInvalidatedCache uses shared memory for nonces." + bug: "366552454" +} diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 933c336c1359..df1028e9e04c 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -716,8 +716,8 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW mCurrentSize); } else { applyContent(null, false, e); + mLastExecutionSignal = null; } - mLastExecutionSignal = null; } } diff --git a/core/java/android/appwidget/OWNERS b/core/java/android/appwidget/OWNERS index 191083303769..0e85d5bd7a27 100644 --- a/core/java/android/appwidget/OWNERS +++ b/core/java/android/appwidget/OWNERS @@ -3,3 +3,5 @@ sihua@google.com pinyaoting@google.com suprabh@google.com sunnygoyal@google.com +zakcohen@google.com +shamalip@google.com diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig index ac9263c2cab5..3839b5fa2599 100644 --- a/core/java/android/appwidget/flags.aconfig +++ b/core/java/android/appwidget/flags.aconfig @@ -74,3 +74,12 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_smaller_app_widget_radius" + namespace: "app_widgets" + description: "Updates system corner radius for app widgets to 24.dp instead of 28.dp" + bug: "373351337" + is_exported: true + is_fixed_read_only: true +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e8cec70ef6a8..66ef004c5298 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -11672,6 +11672,7 @@ public class Intent implements Parcelable, Cloneable { Log.w(TAG, "Failure filling in extras", e); } } + mCreatorTokenInfo = other.mCreatorTokenInfo; if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT && other.mContentUserHint != UserHandle.USER_CURRENT) { mContentUserHint = other.mContentUserHint; @@ -12225,6 +12226,13 @@ public class Intent implements Parcelable, Cloneable { } /** @hide */ + public void removeCreatorToken() { + if (mCreatorTokenInfo != null) { + mCreatorTokenInfo.mCreatorToken = null; + } + } + + /** @hide */ public @Nullable IBinder getCreatorToken() { return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken; } @@ -12251,7 +12259,7 @@ public class Intent implements Parcelable, Cloneable { public void collectExtraIntentKeys() { if (!isPreventIntentRedirectEnabled()) return; - if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { + if (mExtras != null && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { if (mExtras.get(key) instanceof Intent) { if (mCreatorTokenInfo == null) { @@ -12833,6 +12841,8 @@ public class Intent implements Parcelable, Cloneable { private boolean isImageCaptureIntent() { return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction) || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction) + || MediaStore.ACTION_MOTION_PHOTO_CAPTURE.equals(mAction) + || MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE.equals(mAction) || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction)); } diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index c911326ccffd..ecea47944c72 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -94,9 +94,9 @@ interface IPackageInstaller { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + @EnforcePermission("VERIFICATION_AGENT") int getVerificationPolicy(); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") + @EnforcePermission("VERIFICATION_AGENT") boolean setVerificationPolicy(int policy); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e985f88f38fc..47d9f09a5727 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4863,6 +4863,64 @@ public abstract class PackageManager { public static final String FEATURE_CONTEXTUAL_SEARCH_HELPER = "android.software.contextualsearch"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: This device + * supports XR input from XR controllers. + */ + @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_XR_INPUT_CONTROLLER = + "android.hardware.xr.input.controller"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: This device + * supports XR input from the user's hands. + */ + @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_XR_INPUT_HAND_TRACKING = + "android.hardware.xr.input.hand_tracking"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: This device + * supports XR input from the user's eye gaze. + */ + @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_XR_INPUT_EYE_TRACKING = + "android.hardware.xr.input.eye_tracking"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: This device + * supports <a href="https://www.khronos.org/openxr/">OpenXR</a>. The feature version indicates + * the highest version of OpenXR supported by the device using the following encoding: + * <ul> + * <li> Major version in bits 31-16</li> + * <li> Minor version in bits 15-0</li> + * </ul> + * This is the same encoding as the top 32 bits of an {@code XrVersion}. + * <p> + * Example: OpenXR 1.1 support is encoded as 0x00010001. + */ + @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_XR_API_OPENXR = + "android.software.xr.api.openxr"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: This device + * supports the Android XR Spatial APIs. The feature version indicates the highest version of + * the Android XR Spatial APIs supported by the device. + * + * <p>Also see <a href="https://developer.android.com/xr">Getting started with Spatializing + * your app</a>. + */ + // TODO(b/374330735): update public documentation once link content is finalized + @FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_XR_API_SPATIAL = + "android.software.xr.api.spatial"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 5b38942d468d..6f70586881be 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -297,14 +297,6 @@ flag { } flag { - name: "get_packages_from_launcher_apps" - namespace: "package_manager_service" - description: "Feature flag to provide the new methods within launcher apps class to get packages." - bug: "363324203" - is_fixed_read_only: true -} - -flag { name: "remove_cross_user_permission_hack" namespace: "package_manager_service" description: "Feature flag to remove hack code of using PackageManager.MATCH_ANY_USER flag without cross user permission." @@ -328,6 +320,13 @@ flag { } flag { + name: "sdk_dependency_installer" + namespace: "package_manager_service" + description: "Feature flag to enable installation of missing sdk dependency of app" + bug: "370822870" +} + +flag { name: "include_feature_flags_in_package_cacher" namespace: "package_manager_service" description: "Include feature flag status when determining hits or misses in PackageCacher." @@ -342,3 +341,11 @@ flag { bug: "292261144" is_fixed_read_only: true } + +flag { + name: "change_launcher_badging" + namespace: "package_manager_service" + description: "Feature flag to introduce a new way to change the launcher badging." + bug: "364760703" + is_fixed_read_only: true +} diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java index 19a13db15b05..4220590a6943 100644 --- a/core/java/android/content/pm/parsing/ApkLite.java +++ b/core/java/android/content/pm/parsing/ApkLite.java @@ -28,6 +28,7 @@ import android.content.pm.VerifierInfo; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -141,6 +142,21 @@ public class ApkLite { private final boolean mIsSdkLibrary; /** + * List of SDK names used by this apk. + */ + private final @NonNull List<String> mUsesSdkLibraries; + + /** + * List of SDK major versions used by this apk. + */ + private final @Nullable long[] mUsesSdkLibrariesVersionsMajor; + + /** + * List of SDK certificates used by this apk. + */ + private final @Nullable String[][] mUsesSdkLibrariesCertDigests; + + /** * Indicates if this system app can be updated. */ private final boolean mUpdatableSystem; @@ -167,7 +183,9 @@ public class ApkLite { String requiredSystemPropertyName, String requiredSystemPropertyValue, int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy, Set<String> requiredSplitTypes, Set<String> splitTypes, - boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem, + boolean hasDeviceAdminReceiver, boolean isSdkLibrary, + List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor, + String[][] usesSdkLibrariesCertDigests, boolean updatableSystem, String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) { mPath = path; mPackageName = packageName; @@ -202,6 +220,9 @@ public class ApkLite { mRollbackDataPolicy = rollbackDataPolicy; mHasDeviceAdminReceiver = hasDeviceAdminReceiver; mIsSdkLibrary = isSdkLibrary; + mUsesSdkLibraries = usesSdkLibraries; + mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor; + mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests; mUpdatableSystem = updatableSystem; mEmergencyInstaller = emergencyInstaller; mArchivedPackage = null; @@ -242,6 +263,9 @@ public class ApkLite { mRollbackDataPolicy = 0; mHasDeviceAdminReceiver = false; mIsSdkLibrary = false; + mUsesSdkLibraries = Collections.emptyList(); + mUsesSdkLibrariesVersionsMajor = null; + mUsesSdkLibrariesCertDigests = null; mUpdatableSystem = true; mEmergencyInstaller = null; mArchivedPackage = archivedPackage; @@ -555,6 +579,30 @@ public class ApkLite { } /** + * List of SDK names used by this apk. + */ + @DataClass.Generated.Member + public @NonNull List<String> getUsesSdkLibraries() { + return mUsesSdkLibraries; + } + + /** + * List of SDK major versions used by this apk. + */ + @DataClass.Generated.Member + public @Nullable long[] getUsesSdkLibrariesVersionsMajor() { + return mUsesSdkLibrariesVersionsMajor; + } + + /** + * List of SDK certificates used by this apk. + */ + @DataClass.Generated.Member + public @Nullable String[][] getUsesSdkLibrariesCertDigests() { + return mUsesSdkLibrariesCertDigests; + } + + /** * Indicates if this system app can be updated. */ @DataClass.Generated.Member @@ -584,10 +632,10 @@ public class ApkLite { } @DataClass.Generated( - time = 1728333566322L, + time = 1729247366948L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 1a7f628ae61c..50d875845b2f 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -32,6 +32,7 @@ import android.content.pm.parsing.result.ParseResult; import android.content.res.ApkAssets; import android.content.res.XmlResourceParser; import android.os.Build; +import android.os.SystemProperties; import android.os.Trace; import android.text.TextUtils; import android.util.ArrayMap; @@ -44,6 +45,7 @@ import com.android.internal.pm.pkg.component.flags.Flags; import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; +import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -88,6 +90,7 @@ public class ApkLiteParseUtils { private static final String TAG_USES_SDK = "uses-sdk"; private static final String TAG_USES_SPLIT = "uses-split"; private static final String TAG_MANIFEST = "manifest"; + private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library"; private static final String TAG_SDK_LIBRARY = "sdk-library"; private static final int SDK_VERSION = Build.VERSION.SDK_INT; private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; @@ -460,6 +463,9 @@ public class ApkLiteParseUtils { boolean hasDeviceAdminReceiver = false; boolean isSdkLibrary = false; + List<String> usesSdkLibraries = new ArrayList<>(); + long[] usesSdkLibrariesVersionsMajor = new long[0]; + String[][] usesSdkLibrariesCertDigests = new String[0][0]; List<SharedLibraryInfo> declaredLibraries = new ArrayList<>(); // Only search the tree when the tag is the direct child of <manifest> tag @@ -523,6 +529,57 @@ public class ApkLiteParseUtils { hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser, hasBindDeviceAdminPermission); break; + case TAG_USES_SDK_LIBRARY: + String usesSdkLibName = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "name"); + long usesSdkLibVersionMajor = parser.getAttributeIntValue( + ANDROID_RES_NAMESPACE, "versionMajor", -1); + String usesSdkCertDigest = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "certDigest"); + + if (usesSdkLibName == null || usesSdkLibName.isBlank() + || usesSdkLibVersionMajor < 0) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad uses-sdk-library declaration name: " + + usesSdkLibName + + " version: " + usesSdkLibVersionMajor); + } + + if (usesSdkLibraries.contains(usesSdkLibName)) { + return input.error( + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Bad uses-sdk-library declaration. Depending on" + + " multiple versions of SDK library: " + + usesSdkLibName); + } + + usesSdkLibraries.add(usesSdkLibName); + usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong( + usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor, + /*allowDuplicates=*/ true); + + // We allow ":" delimiters in the SHA declaration as this is the format + // emitted by the certtool making it easy for developers to copy/paste. + // TODO(372862145): Add test for this replacement + usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase(); + + if ("".equals(usesSdkCertDigest)) { + // Test-only uses-sdk-library empty certificate digest override. + usesSdkCertDigest = SystemProperties.get( + "debug.pm.uses_sdk_library_default_cert_digest", ""); + // Validate the overridden digest. + try { + HexEncoding.decode(usesSdkCertDigest, false); + } catch (IllegalArgumentException e) { + usesSdkCertDigest = ""; + } + } + // TODO(372862145): Add support for multiple signer + usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class, + usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest}, + /*allowDuplicates=*/ true); + break; case TAG_SDK_LIBRARY: isSdkLibrary = true; // Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full @@ -534,7 +591,7 @@ public class ApkLiteParseUtils { if (sdkLibName == null || sdkLibVersionMajor < 0) { return input.error( PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, - "Bad uses-sdk-library declaration name: " + sdkLibName + "Bad sdk-library declaration name: " + sdkLibName + " version: " + sdkLibVersionMajor); } declaredLibraries.add(new SharedLibraryInfo( @@ -694,8 +751,9 @@ public class ApkLiteParseUtils { overlayIsStatic, overlayPriority, requiredSystemPropertyName, requiredSystemPropertyValue, minSdkVersion, targetSdkVersion, rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second, - hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller, - declaredLibraries)); + hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries, + usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests, + updatableSystem, emergencyInstaller, declaredLibraries)); } private static boolean isDeviceAdminReceiver( diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java index 9a2ee7fe4cc6..79c597327f5a 100644 --- a/core/java/android/content/pm/parsing/PackageLite.java +++ b/core/java/android/content/pm/parsing/PackageLite.java @@ -115,6 +115,12 @@ public class PackageLite { */ private final boolean mIsSdkLibrary; + private final @NonNull List<String> mUsesSdkLibraries; + + private final @Nullable long[] mUsesSdkLibrariesVersionsMajor; + + private final @Nullable String[][] mUsesSdkLibrariesCertDigests; + private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries; /** @@ -149,6 +155,9 @@ public class PackageLite { mSplitRequired = (baseApk.isSplitRequired() || hasAnyRequiredSplitTypes()); mProfileableByShell = baseApk.isProfileableByShell(); mIsSdkLibrary = baseApk.isIsSdkLibrary(); + mUsesSdkLibraries = baseApk.getUsesSdkLibraries(); + mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor(); + mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests(); mSplitNames = splitNames; mSplitTypes = splitTypes; mIsFeatureSplits = isFeatureSplits; @@ -438,6 +447,21 @@ public class PackageLite { } @DataClass.Generated.Member + public @NonNull List<String> getUsesSdkLibraries() { + return mUsesSdkLibraries; + } + + @DataClass.Generated.Member + public @Nullable long[] getUsesSdkLibrariesVersionsMajor() { + return mUsesSdkLibrariesVersionsMajor; + } + + @DataClass.Generated.Member + public @Nullable String[][] getUsesSdkLibrariesCertDigests() { + return mUsesSdkLibrariesCertDigests; + } + + @DataClass.Generated.Member public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() { return mDeclaredLibraries; } @@ -451,10 +475,10 @@ public class PackageLite { } @DataClass.Generated( - time = 1728333569917L, + time = 1729248757933L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl index 66caf2d0fec0..2ab745205193 100644 --- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl +++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl @@ -24,16 +24,9 @@ import android.os.PersistableBundle; * @hide */ interface IVerificationSessionInterface { - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") long getTimeoutTime(int verificationId); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") long extendTimeRemaining(int verificationId, long additionalMs); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") boolean setVerificationPolicy(int verificationId, int policy); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") void reportVerificationIncomplete(int verificationId, int reason); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") - void reportVerificationComplete(int verificationId, in VerificationStatus status); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)") - void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response); + void reportVerificationComplete(int verificationId, in VerificationStatus status, in @nullable PersistableBundle extensionResponse); }
\ No newline at end of file diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java index 4ade21198f37..97f78e0978fa 100644 --- a/core/java/android/content/pm/verify/pkg/VerificationSession.java +++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java @@ -19,7 +19,6 @@ package android.content.pm.verify.pkg; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.pm.Flags; import android.content.pm.PackageInstaller; @@ -166,8 +165,8 @@ public final class VerificationSession implements Parcelable { /** * Get the value of Clock.elapsedRealtime() at which time this verification * will timeout as incomplete if no other verification response is provided. + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime() { try { return mSession.getTimeoutTime(mId); @@ -190,8 +189,8 @@ public final class VerificationSession implements Parcelable { /** * Override the verification policy for this session. * @return True if the override was successful, False otherwise. + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) { if (mVerificationPolicy == policy) { // No effective policy change @@ -215,8 +214,8 @@ public final class VerificationSession implements Parcelable { * This may be called multiple times. If the request would bypass any max * duration by the system, the method will return a lower value than the * requested amount that indicates how much the time was extended. + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long additionalMs) { try { return mSession.extendTimeRemaining(mId, additionalMs); @@ -227,9 +226,9 @@ public final class VerificationSession implements Parcelable { /** * Report to the system that verification could not be completed along - * with an approximate reason to pass on to the installer. + * with an approximate reason to pass on to the installer.] + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) { try { mSession.reportVerificationIncomplete(mId, reason); @@ -242,11 +241,11 @@ public final class VerificationSession implements Parcelable { * Report to the system that the verification has completed and the * install process may act on that status to either block in the case * of failure or continue to process the install in the case of success. + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull VerificationStatus status) { try { - mSession.reportVerificationComplete(mId, status); + mSession.reportVerificationComplete(mId, status, /* extensionResponse= */ null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -256,12 +255,12 @@ public final class VerificationSession implements Parcelable { * Same as {@link #reportVerificationComplete(VerificationStatus)}, but also provide * a result to the extension params provided in the request, which will be passed to the * installer in the installation result. + * @throws SecurityException if the caller is not the current verifier bound by the system. */ - @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull VerificationStatus status, - @NonNull PersistableBundle response) { + @NonNull PersistableBundle extensionResponse) { try { - mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response); + mSession.reportVerificationComplete(mId, status, extensionResponse); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index 93958443c01b..44115c832c17 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -66,6 +66,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { DS_FP32UI8, S_UI8, YCBCR_P010, + YCBCR_P210, R_8, R_16, RG_1616, @@ -111,6 +112,16 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { * little-endian value, with the lower 6 bits set to zero. */ public static final int YCBCR_P010 = 0x36; + /** + * <p>Android YUV P210 format.</p> + * + * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a WxH CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. + */ + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int YCBCR_P210 = 0x3c; + /** Format: 8 bits red */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int R_8 = 0x38; diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java index 57f8a4ece304..c9c6d6d08ed2 100644 --- a/core/java/android/hardware/LutProperties.java +++ b/core/java/android/hardware/LutProperties.java @@ -30,7 +30,7 @@ import java.lang.annotation.RetentionPolicy; */ public final class LutProperties { private final @Dimension int mDimension; - private final long mSize; + private final int mSize; private final @SamplingKey int[] mSamplingKeys; @Retention(RetentionPolicy.SOURCE) @@ -68,7 +68,7 @@ public final class LutProperties { /** * @return the size of the Lut. */ - public long getSize() { + public int getSize() { return mSize; } @@ -83,7 +83,7 @@ public final class LutProperties { } /* use in the native code */ - private LutProperties(@Dimension int dimension, long size, @SamplingKey int[] samplingKeys) { + private LutProperties(@Dimension int dimension, int size, @SamplingKey int[] samplingKeys) { if (dimension != ONE_DIMENSION || dimension != THREE_DIMENSION) { throw new IllegalArgumentException("The dimension is either 1 or 3!"); } diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig index 047d1fa4f49a..26ffa11823d8 100644 --- a/core/java/android/hardware/biometrics/flags.aconfig +++ b/core/java/android/hardware/biometrics/flags.aconfig @@ -39,3 +39,11 @@ flag { description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations" bug: "322081563" } + +flag { + name: "screen_off_unlock_udfps" + is_exported: true + namespace: "biometrics_integration" + description: "This flag controls Whether to enable fp unlock when screen turns off on udfps devices" + bug: "373792870" +} diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 37983df75f2b..1137e1e89f67 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -4247,7 +4247,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * This key will only be present for devices which advertise the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}</p> + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}.</p> * <p><b>Units</b>: Pixel coordinates on the image sensor</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> * @@ -4273,7 +4273,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * This key will only be present for devices which advertise the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}</p> + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}.</p> * <p><b>Units</b>: Pixels</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> * @@ -4298,7 +4298,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * This key will only be present for devices which advertise the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}</p> + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}.</p> * <p><b>Units</b>: Pixel coordinates on the image sensor</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> * @@ -4332,7 +4332,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <ul> * <li>This key will be present if * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}, since RAW + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, since RAW * images may not necessarily have a regular bayer pattern when * {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}} is set to * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</li> diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index a193ee10b6e6..a5c5a9952879 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1465,7 +1465,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}} + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -1715,7 +1715,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}, + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -1940,7 +1940,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}, + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -3285,8 +3285,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>For camera devices with the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}</p> - * <p>{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index e5ca46ab0a72..a6bdb3f1bb07 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -866,7 +866,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}} + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -1366,7 +1366,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}, + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -2002,7 +2002,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}, + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to @@ -3953,8 +3953,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>For camera devices with the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}}</p> - * <p>{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p> @@ -5137,6 +5137,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * height dimensions are given in {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}. * This may include hot pixels that lie outside of the active array * bounds given by {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p> + * <p>For camera devices with the + * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } + * capability or devices where + * {@link CameraCharacteristics#getAvailableCaptureRequestKeys } + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, + * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.pixelArraySizeMaximumResolution} will be used as the + * pixel array size if the corresponding request sets {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} to + * {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION }.</p> * <p><b>Range of valid values:</b><br></p> * <p>n <= number of pixels on the sensor. * The <code>(x, y)</code> coordinates must be bounded by @@ -5145,6 +5153,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE + * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION + * @see CaptureRequest#SENSOR_PIXEL_MODE */ @PublicKey @NonNull @@ -5798,8 +5808,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>For camera devices with the * {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR } * capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys } - * lists {@link CaptureRequest#SENSOR_PIXEL_MODE {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}} - * , the current active physical device + * lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}, + * the current active physical device * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.activeArraySizeMaximumResolution} / * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION android.sensor.info.preCorrectionActiveArraySizeMaximumResolution} must be used as the * coordinate system for requests where {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 47541ca16cda..59a602ca092d 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -18,6 +18,7 @@ package android.hardware.display; import android.annotation.TestApi; import android.content.Context; +import android.hardware.biometrics.Flags; import android.os.Build; import android.os.SystemProperties; import android.provider.Settings; @@ -41,6 +42,7 @@ public class AmbientDisplayConfiguration { private final Context mContext; private final boolean mAlwaysOnByDefault; private final boolean mPickupGestureEnabledByDefault; + private final boolean mScreenOffUdfpsEnabledByDefault; /** Copied from android.provider.Settings.Secure since these keys are hidden. */ private static final String[] DOZE_SETTINGS = { @@ -68,6 +70,8 @@ public class AmbientDisplayConfiguration { mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled); mPickupGestureEnabledByDefault = mContext.getResources().getBoolean(R.bool.config_dozePickupGestureEnabled); + mScreenOffUdfpsEnabledByDefault = + mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_enabled); } /** @hide */ @@ -146,7 +150,9 @@ public class AmbientDisplayConfiguration { /** @hide */ public boolean screenOffUdfpsEnabled(int user) { return !TextUtils.isEmpty(udfpsLongPressSensorType()) - && boolSettingDefaultOff("screen_off_udfps_enabled", user); + && ((mScreenOffUdfpsEnabledByDefault && Flags.screenOffUnlockUdfps()) + ? boolSettingDefaultOn("screen_off_udfps_enabled", user) + : boolSettingDefaultOff("screen_off_udfps_enabled", user)); } /** @hide */ diff --git a/core/java/android/hardware/fingerprint/FingerprintCallback.java b/core/java/android/hardware/fingerprint/FingerprintCallback.java index 24e9f9ddef77..e4fbe6e09709 100644 --- a/core/java/android/hardware/fingerprint/FingerprintCallback.java +++ b/core/java/android/hardware/fingerprint/FingerprintCallback.java @@ -189,7 +189,7 @@ public class FingerprintCallback { mEnrollmentCallback.onAcquired(acquireInfo == FINGERPRINT_ACQUIRED_GOOD); } final String msg = getAcquiredString(context, acquireInfo, vendorCode); - if (msg == null || msg.isEmpty()) { + if (msg == null) { return; } // emulate HAL 2.1 behavior and send real acquiredInfo diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 7f1cac08b430..590c4d661076 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1517,7 +1517,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) { switch (acquireInfo) { - case FINGERPRINT_ACQUIRED_GOOD: + case FINGERPRINT_ACQUIRED_GOOD, FINGERPRINT_ACQUIRED_START: return null; case FINGERPRINT_ACQUIRED_PARTIAL: return context.getString( @@ -1546,13 +1546,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case FINGERPRINT_ACQUIRED_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.fingerprint_acquired_vendor); - if (vendorCode < msgArray.length) { + if (vendorCode < msgArray.length && !msgArray[vendorCode].isEmpty()) { return msgArray[vendorCode]; } } - break; - case FINGERPRINT_ACQUIRED_START: - return null; } Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode); return null; diff --git a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl index 7cf8795085e3..fc7151940bd7 100644 --- a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl +++ b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl @@ -26,4 +26,10 @@ parcelable AidlKeyGestureEvent { int action; int displayId; int flags; + + // App launch parameters: only set when gestureType = KEY_GESTURE_TYPE_LAUNCH_APPLICATION + String appLaunchCategory; + String appLaunchRole; + String appLaunchPackageName; + String appLaunchClassName; } diff --git a/core/java/android/hardware/input/AppLaunchData.java b/core/java/android/hardware/input/AppLaunchData.java new file mode 100644 index 000000000000..43186f5db415 --- /dev/null +++ b/core/java/android/hardware/input/AppLaunchData.java @@ -0,0 +1,176 @@ +/* + * Copyright 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.hardware.input; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.text.TextUtils; + +import java.util.Objects; + +/** + * Provides data for launching an application. + * + * @hide + */ +public interface AppLaunchData { + + /** Creates AppLaunchData for the provided category */ + @NonNull + static AppLaunchData createLaunchDataForCategory(@NonNull String category) { + return new CategoryData(category); + } + + /** Creates AppLaunchData for the provided role */ + @NonNull + static AppLaunchData createLaunchDataForRole(@NonNull String role) { + return new RoleData(role); + } + + /** Creates AppLaunchData for the target package name and class name */ + @NonNull + static AppLaunchData createLaunchDataForComponent(@NonNull String packageName, + @NonNull String className) { + return new ComponentData(packageName, className); + } + + @Nullable + static AppLaunchData createLaunchData(@Nullable String category, @Nullable String role, + @Nullable String packageName, @Nullable String className) { + if (!TextUtils.isEmpty(category)) { + return new CategoryData(category); + } + if (!TextUtils.isEmpty(role)) { + return new RoleData(role); + } + if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) { + return new ComponentData(packageName, className); + } + return null; + } + + /** Intent category based app launch data */ + class CategoryData implements AppLaunchData { + @NonNull + private final String mCategory; + public CategoryData(@NonNull String category) { + mCategory = category; + } + + @NonNull + public String getCategory() { + return mCategory; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CategoryData that)) return false; + return Objects.equals(mCategory, that.mCategory); + } + + @Override + public int hashCode() { + return Objects.hash(mCategory); + } + + @Override + public String toString() { + return "CategoryData{" + + "mCategory='" + mCategory + '\'' + + '}'; + } + } + + /** Role based app launch data */ + class RoleData implements AppLaunchData { + @NonNull + private final String mRole; + public RoleData(@NonNull String role) { + mRole = role; + } + + @NonNull + public String getRole() { + return mRole; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RoleData roleData)) return false; + return Objects.equals(mRole, roleData.mRole); + } + + @Override + public int hashCode() { + return Objects.hash(mRole); + } + + @Override + public String toString() { + return "RoleData{" + + "mRole='" + mRole + '\'' + + '}'; + } + } + + /** Target application launch data */ + class ComponentData implements AppLaunchData { + @NonNull + private final String mPackageName; + + @NonNull + private final String mClassName; + + public ComponentData(@NonNull String packageName, @NonNull String className) { + mPackageName = packageName; + mClassName = className; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + @NonNull + public String getClassName() { + return mClassName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ComponentData that)) return false; + return Objects.equals(mPackageName, that.mPackageName) && Objects.equals( + mClassName, that.mClassName); + } + + @Override + public int hashCode() { + return Objects.hash(mPackageName, mClassName); + } + + @Override + public String toString() { + return "ComponentData{" + + "mPackageName='" + mPackageName + '\'' + + ", mClassName='" + mClassName + '\'' + + '}'; + } + } +} diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 2df541818e3d..ee1a6aba2ab5 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -19,6 +19,8 @@ package android.hardware.input; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.role.RoleManager; +import android.content.Intent; import android.view.Display; import android.view.KeyCharacterMap; @@ -26,6 +28,7 @@ import com.android.internal.util.FrameworkStatsLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Provides information about the keyboard gesture event being triggered by an external keyboard. @@ -37,6 +40,9 @@ public final class KeyGestureEvent { @NonNull private AidlKeyGestureEvent mKeyGestureEvent; + private static final int LOG_EVENT_UNSPECIFIED = + FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED; + public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0; public static final int KEY_GESTURE_TYPE_HOME = 1; public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2; @@ -76,6 +82,8 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_SLEEP = 36; public static final int KEY_GESTURE_TYPE_WAKEUP = 37; public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 38; + // TODO(b/280423320): Remove "LAUNCH_DEFAULT_..." gestures and rely on launch intent to find + // the correct logging event. public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 39; public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 40; public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 41; @@ -88,7 +96,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 48; public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 49; public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 50; - public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 51; + public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION = 51; public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 52; public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 53; public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 54; @@ -100,6 +108,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60; public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61; public static final int KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY = 62; + public static final int KEY_GESTURE_TYPE_TOGGLE_TALKBACK = 63; public static final int FLAG_CANCELLED = 1; @@ -165,7 +174,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES, KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER, KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS, - KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME, + KEY_GESTURE_TYPE_LAUNCH_APPLICATION, KEY_GESTURE_TYPE_DESKTOP_MODE, KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION, KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER, @@ -177,6 +186,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, + KEY_GESTURE_TYPE_TOGGLE_TALKBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -208,6 +218,8 @@ public final class KeyGestureEvent { private int mAction = KeyGestureEvent.ACTION_GESTURE_COMPLETE; private int mDisplayId = Display.DEFAULT_DISPLAY; private int mFlags = 0; + @Nullable + private AppLaunchData mAppLaunchData = null; /** * @see KeyGestureEvent#getDeviceId() @@ -266,6 +278,14 @@ public final class KeyGestureEvent { } /** + * @see KeyGestureEvent#getAppLaunchData() + */ + public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) { + mAppLaunchData = appLaunchData; + return this; + } + + /** * Build {@link KeyGestureEvent} */ public KeyGestureEvent build() { @@ -277,6 +297,21 @@ public final class KeyGestureEvent { event.action = mAction; event.displayId = mDisplayId; event.flags = mFlags; + if (mAppLaunchData != null) { + if (mAppLaunchData instanceof AppLaunchData.CategoryData) { + event.appLaunchCategory = + ((AppLaunchData.CategoryData) mAppLaunchData).getCategory(); + } else if (mAppLaunchData instanceof AppLaunchData.RoleData) { + event.appLaunchRole = ((AppLaunchData.RoleData) mAppLaunchData).getRole(); + } else if (mAppLaunchData instanceof AppLaunchData.ComponentData) { + event.appLaunchPackageName = + ((AppLaunchData.ComponentData) mAppLaunchData).getPackageName(); + event.appLaunchClassName = + ((AppLaunchData.ComponentData) mAppLaunchData).getClassName(); + } else { + throw new IllegalArgumentException("AppLaunchData type is invalid!"); + } + } return new KeyGestureEvent(event); } } @@ -313,6 +348,27 @@ public final class KeyGestureEvent { return (mKeyGestureEvent.flags & FLAG_CANCELLED) != 0; } + public int getLogEvent() { + if (getKeyGestureType() == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) { + return getLogEventFromLaunchAppData(getAppLaunchData()); + } + return keyGestureTypeToLogEvent(getKeyGestureType()); + } + + /** + * @return Launch app data associated with the event, only if key gesture type is + * {@code KEY_GESTURE_TYPE_LAUNCH_APPLICATION} + */ + @Nullable + public AppLaunchData getAppLaunchData() { + if (mKeyGestureEvent.gestureType != KEY_GESTURE_TYPE_LAUNCH_APPLICATION) { + return null; + } + return AppLaunchData.createLaunchData(mKeyGestureEvent.appLaunchCategory, + mKeyGestureEvent.appLaunchRole, mKeyGestureEvent.appLaunchPackageName, + mKeyGestureEvent.appLaunchClassName); + } + @Override public String toString() { return "KeyGestureEvent { " @@ -322,7 +378,8 @@ public final class KeyGestureEvent { + "keyGestureType = " + keyGestureTypeToString(mKeyGestureEvent.gestureType) + ", " + "action = " + mKeyGestureEvent.action + ", " + "displayId = " + mKeyGestureEvent.displayId + ", " - + "flags = " + mKeyGestureEvent.flags + + "flags = " + mKeyGestureEvent.flags + ", " + + "appLaunchData = " + getAppLaunchData() + " }"; } @@ -337,7 +394,11 @@ public final class KeyGestureEvent { && mKeyGestureEvent.gestureType == that.mKeyGestureEvent.gestureType && mKeyGestureEvent.action == that.mKeyGestureEvent.action && mKeyGestureEvent.displayId == that.mKeyGestureEvent.displayId - && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags; + && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags + && Objects.equals(mKeyGestureEvent.appLaunchCategory, that.mKeyGestureEvent.appLaunchCategory) + && Objects.equals(mKeyGestureEvent.appLaunchRole, that.mKeyGestureEvent.appLaunchRole) + && Objects.equals(mKeyGestureEvent.appLaunchPackageName, that.mKeyGestureEvent.appLaunchPackageName) + && Objects.equals(mKeyGestureEvent.appLaunchClassName, that.mKeyGestureEvent.appLaunchClassName); } @Override @@ -350,13 +411,21 @@ public final class KeyGestureEvent { _hash = 31 * _hash + mKeyGestureEvent.action; _hash = 31 * _hash + mKeyGestureEvent.displayId; _hash = 31 * _hash + mKeyGestureEvent.flags; + _hash = 31 * _hash + (mKeyGestureEvent.appLaunchCategory != null + ? mKeyGestureEvent.appLaunchCategory.hashCode() : 0); + _hash = 31 * _hash + (mKeyGestureEvent.appLaunchRole != null + ? mKeyGestureEvent.appLaunchRole.hashCode() : 0); + _hash = 31 * _hash + (mKeyGestureEvent.appLaunchPackageName != null + ? mKeyGestureEvent.appLaunchPackageName.hashCode() : 0); + _hash = 31 * _hash + (mKeyGestureEvent.appLaunchClassName != null + ? mKeyGestureEvent.appLaunchClassName.hashCode() : 0); return _hash; } /** * Convert KeyGestureEvent type to corresponding log event got KeyboardSystemsEvent */ - public static int keyGestureTypeToLogEvent(@KeyGestureType int value) { + private static int keyGestureTypeToLogEvent(@KeyGestureType int value) { switch (value) { case KEY_GESTURE_TYPE_HOME: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME; @@ -458,14 +527,79 @@ public final class KeyGestureEvent { return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER; case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS; - case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME: + case KEY_GESTURE_TYPE_LAUNCH_APPLICATION: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME; case KEY_GESTURE_TYPE_DESKTOP_MODE: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE; case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION: return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION; default: - return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED; + return LOG_EVENT_UNSPECIFIED; + } + } + + /** + * Find Log event type corresponding to app launch data. + * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found + */ + private static int getLogEventFromLaunchAppData(@Nullable AppLaunchData data) { + if (data == null) { + return LOG_EVENT_UNSPECIFIED; + } + if (data instanceof AppLaunchData.CategoryData) { + return getLogEventFromSelectorCategory( + ((AppLaunchData.CategoryData) data).getCategory()); + } else if (data instanceof AppLaunchData.RoleData) { + return getLogEventFromRole(((AppLaunchData.RoleData) data).getRole()); + } else if (data instanceof AppLaunchData.ComponentData) { + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME; + } else { + throw new IllegalArgumentException("AppLaunchData type is invalid!"); + } + } + + private static int getLogEventFromSelectorCategory(@NonNull String category) { + switch (category) { + case Intent.CATEGORY_APP_BROWSER: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER; + case Intent.CATEGORY_APP_EMAIL: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL; + case Intent.CATEGORY_APP_CONTACTS: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS; + case Intent.CATEGORY_APP_CALENDAR: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR; + case Intent.CATEGORY_APP_CALCULATOR: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR; + case Intent.CATEGORY_APP_MUSIC: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC; + case Intent.CATEGORY_APP_MAPS: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS; + case Intent.CATEGORY_APP_MESSAGING: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING; + case Intent.CATEGORY_APP_GALLERY: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY; + case Intent.CATEGORY_APP_FILES: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES; + case Intent.CATEGORY_APP_WEATHER: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER; + case Intent.CATEGORY_APP_FITNESS: + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS; + default: + return LOG_EVENT_UNSPECIFIED; + } + } + + /** + * Find Log event corresponding to the provide system role name. + * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found. + */ + private static int getLogEventFromRole(@NonNull String role) { + if (RoleManager.ROLE_BROWSER.equals(role)) { + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER; + } else if (RoleManager.ROLE_SMS.equals(role)) { + return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING; + } else { + return LOG_EVENT_UNSPECIFIED; } } @@ -575,8 +709,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER"; case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS: return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS"; - case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME: - return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME"; + case KEY_GESTURE_TYPE_LAUNCH_APPLICATION: + return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION"; case KEY_GESTURE_TYPE_DESKTOP_MODE: return "KEY_GESTURE_TYPE_DESKTOP_MODE"; case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION: @@ -597,6 +731,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT"; case KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS"; + case KEY_GESTURE_TYPE_TOGGLE_TALKBACK: + return "KEY_GESTURE_TYPE_TOGGLE_TALKBACK"; default: return Integer.toHexString(value); } diff --git a/core/java/android/hardware/input/KeyGlyphMap.java b/core/java/android/hardware/input/KeyGlyphMap.java index 49c47a2ad4c4..b517a635c9c1 100644 --- a/core/java/android/hardware/input/KeyGlyphMap.java +++ b/core/java/android/hardware/input/KeyGlyphMap.java @@ -170,6 +170,8 @@ public final class KeyGlyphMap implements Parcelable { return resources.getDrawable(drawableRes, null); } catch (PackageManager.NameNotFoundException ignored) { Log.e(TAG, "Package name not found for " + mComponentName); + } catch (Resources.NotFoundException ignored) { + Log.e(TAG, "Resource not found for " + mComponentName); } return null; } diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index 953086de2d15..538e2fb7fe3e 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -194,23 +194,20 @@ public class ContextHubClient implements Closeable { /** * Sends a reliable message to a nanoapp. * - * This method is similar to {@link ContextHubClient#sendMessageToNanoApp} with the + * <p>This method is similar to {@link ContextHubClient#sendMessageToNanoApp} with the * difference that it expects the message to be acknowledged by CHRE. * - * The transaction succeeds after we received an ACK from CHRE without error. - * In all other cases the transaction will fail. + * <p>The transaction succeeds after we received an ACK from CHRE without error. In all other + * cases the transaction will fail. */ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @NonNull - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) public ContextHubTransaction<Void> sendReliableMessageToNanoApp( @NonNull NanoAppMessage message) { ContextHubTransaction<Void> transaction = new ContextHubTransaction<>(ContextHubTransaction.TYPE_RELIABLE_MESSAGE); - if (!Flags.reliableMessageImplementation() || - !mAttachedHub.supportsReliableMessages() || - message.isBroadcastMessage()) { + if (!mAttachedHub.supportsReliableMessages() || message.isBroadcastMessage()) { transaction.setResponse(new ContextHubTransaction.Response<Void>( ContextHubTransaction.RESULT_FAILED_NOT_SUPPORTED, null)); return transaction; diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java index d9349701bf7d..858ec23ebed8 100644 --- a/core/java/android/hardware/location/ContextHubInfo.java +++ b/core/java/android/hardware/location/ContextHubInfo.java @@ -99,8 +99,7 @@ public class ContextHubInfo implements Parcelable { mSleepPowerDrawMw = 0; mPeakPowerDrawMw = 0; mMaxPacketLengthBytes = contextHub.maxSupportedMessageLengthBytes; - mSupportsReliableMessages = Flags.reliableMessageImplementation() - && contextHub.supportsReliableMessages; + mSupportsReliableMessages = contextHub.supportsReliableMessages; mChrePlatformId = contextHub.chrePlatformId; mChreApiMajorVersion = contextHub.chreApiMajorVersion; mChreApiMinorVersion = contextHub.chreApiMinorVersion; @@ -124,7 +123,6 @@ public class ContextHubInfo implements Parcelable { * * @return whether reliable messages are supported. */ - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) public boolean supportsReliableMessages() { return mSupportsReliableMessages; } @@ -364,22 +362,22 @@ public class ContextHubInfo implements Parcelable { boolean isEqual = false; if (object instanceof ContextHubInfo) { ContextHubInfo other = (ContextHubInfo) object; - isEqual = (other.getId() == mId) - && other.getName().equals(mName) - && other.getVendor().equals(mVendor) - && other.getToolchain().equals(mToolchain) - && (other.getToolchainVersion() == mToolchainVersion) - && (other.getStaticSwVersion() == getStaticSwVersion()) - && (other.getChrePlatformId() == mChrePlatformId) - && (other.getPeakMips() == mPeakMips) - && (other.getStoppedPowerDrawMw() == mStoppedPowerDrawMw) - && (other.getSleepPowerDrawMw() == mSleepPowerDrawMw) - && (other.getPeakPowerDrawMw() == mPeakPowerDrawMw) - && (other.getMaxPacketLengthBytes() == mMaxPacketLengthBytes) - && (!Flags.reliableMessage() - || (other.supportsReliableMessages() == mSupportsReliableMessages)) - && Arrays.equals(other.getSupportedSensors(), mSupportedSensors) - && Arrays.equals(other.getMemoryRegions(), mMemoryRegions); + isEqual = + (other.getId() == mId) + && other.getName().equals(mName) + && other.getVendor().equals(mVendor) + && other.getToolchain().equals(mToolchain) + && (other.getToolchainVersion() == mToolchainVersion) + && (other.getStaticSwVersion() == getStaticSwVersion()) + && (other.getChrePlatformId() == mChrePlatformId) + && (other.getPeakMips() == mPeakMips) + && (other.getStoppedPowerDrawMw() == mStoppedPowerDrawMw) + && (other.getSleepPowerDrawMw() == mSleepPowerDrawMw) + && (other.getPeakPowerDrawMw() == mPeakPowerDrawMw) + && (other.getMaxPacketLengthBytes() == mMaxPacketLengthBytes) + && (other.supportsReliableMessages() == mSupportsReliableMessages) + && Arrays.equals(other.getSupportedSensors(), mSupportedSensors) + && Arrays.equals(other.getMemoryRegions(), mMemoryRegions); } return isEqual; diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 218b02315fc8..6284e7061b88 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -750,9 +750,7 @@ public final class ContextHubManager { executor.execute( () -> { callback.onMessageFromNanoApp(client, message); - if (Flags.reliableMessage() - && Flags.reliableMessageImplementation() - && message.isReliable()) { + if (message.isReliable()) { client.reliableMessageCallbackFinished( message.getMessageSequenceNumber(), ErrorCode.OK); } else { diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java index 4060f4c1b462..bd87b5cb6d54 100644 --- a/core/java/android/hardware/location/ContextHubTransaction.java +++ b/core/java/android/hardware/location/ContextHubTransaction.java @@ -69,7 +69,6 @@ public class ContextHubTransaction<T> { public static final int TYPE_ENABLE_NANOAPP = 2; public static final int TYPE_DISABLE_NANOAPP = 3; public static final int TYPE_QUERY_NANOAPPS = 4; - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) public static final int TYPE_RELIABLE_MESSAGE = 5; /** @@ -123,10 +122,8 @@ public class ContextHubTransaction<T> { * Failure mode when the Context Hub HAL was not available. */ public static final int RESULT_FAILED_HAL_UNAVAILABLE = 8; - /** - * Failure mode when the operation is not supported. - */ - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) + + /** Failure mode when the operation is not supported. */ public static final int RESULT_FAILED_NOT_SUPPORTED = 9; /** @@ -232,11 +229,8 @@ public class ContextHubTransaction<T> { return upperCase ? "Disable" : "disable"; case ContextHubTransaction.TYPE_QUERY_NANOAPPS: return upperCase ? "Query" : "query"; - case ContextHubTransaction.TYPE_RELIABLE_MESSAGE: { - if (Flags.reliableMessage()) { - return upperCase ? "Reliable Message" : "reliable message"; - } - } + case ContextHubTransaction.TYPE_RELIABLE_MESSAGE: + return upperCase ? "Reliable Message" : "reliable message"; default: return upperCase ? "Unknown" : "unknown"; } diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java index ec0adda38d10..32b9283ecd2e 100644 --- a/core/java/android/hardware/location/NanoAppMessage.java +++ b/core/java/android/hardware/location/NanoAppMessage.java @@ -97,7 +97,7 @@ public final class NanoAppMessage implements Parcelable { /** * Creates a NanoAppMessage object sent from a nanoapp. * - * This factory method is intended only to be used by the Context Hub Service when delivering + * <p>This factory method is intended only to be used by the Context Hub Service when delivering * messages from a nanoapp to clients. * * @param sourceNanoAppId the ID of the nanoapp that the message was sent from @@ -106,12 +106,14 @@ public final class NanoAppMessage implements Parcelable { * @param broadcasted {@code true} if the message was broadcasted, {@code false} otherwise * @param isReliable if the NanoAppMessage is reliable * @param messageSequenceNumber the message sequence number of the NanoAppMessage - * * @return the NanoAppMessage object */ - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) - public static @NonNull NanoAppMessage createMessageFromNanoApp(long sourceNanoAppId, - int messageType, @NonNull byte[] messageBody, boolean broadcasted, boolean isReliable, + public static @NonNull NanoAppMessage createMessageFromNanoApp( + long sourceNanoAppId, + int messageType, + @NonNull byte[] messageBody, + boolean broadcasted, + boolean isReliable, int messageSequenceNumber) { return new NanoAppMessage(sourceNanoAppId, messageType, messageBody, broadcasted, isReliable, messageSequenceNumber); @@ -147,18 +149,18 @@ public final class NanoAppMessage implements Parcelable { /** * Returns if the message is reliable. The default value is {@code false} + * * @return {@code true} if the message is reliable, {@code false} otherwise */ - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) public boolean isReliable() { return mIsReliable; } /** * Returns the message sequence number. The default value is 0 + * * @return the message sequence number of the message */ - @FlaggedApi(Flags.FLAG_RELIABLE_MESSAGE) public int getMessageSequenceNumber() { return mMessageSequenceNumber; } diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java index 9b93e73555f0..6eb9e5845d30 100644 --- a/core/java/android/hardware/radio/RadioAlert.java +++ b/core/java/android/hardware/radio/RadioAlert.java @@ -17,13 +17,19 @@ package android.hardware.radio; import android.annotation.FlaggedApi; +import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -35,8 +41,272 @@ import java.util.Objects; * @hide */ @FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) +@SystemApi public final class RadioAlert implements Parcelable { + /** + * Actionable by all targeted recipients. + */ + public static final int STATUS_ACTUAL = 0; + + /** + * Actionable only by designated exercise participants. + */ + public static final int STATUS_EXERCISE = 1; + + /** + * Technical testing only, all recipients disregard. + */ + public static final int STATUS_TEST = 2; + + /** + * The status of the alert message. + * + * <p>Status is the appropriate handling of the alert message. + * + * @hide + */ + @IntDef(prefix = { "STATUS_" }, value = { + STATUS_ACTUAL, + STATUS_EXERCISE, + STATUS_TEST, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertStatus {} + + /** + * Initial information requiring attention by targeted recipients. + */ + public static final int MESSAGE_TYPE_ALERT = 0; + + /** + * Updates and supersedes the earlier message(s). + */ + public static final int MESSAGE_TYPE_UPDATE = 1; + + /** + * Cancels the earlier message(s). + */ + public static final int MESSAGE_TYPE_CANCEL = 2; + + /** + * The emergency alert message type. + * + * <p>The message type indicates the emergency alert message nature. + * + * @hide + */ + @IntDef(prefix = { "MESSAGE_TYPE_" }, value = { + MESSAGE_TYPE_ALERT, + MESSAGE_TYPE_UPDATE, + MESSAGE_TYPE_CANCEL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertMessageType {} + + /** + * Alert category related to geophysical (inc. landslide). + */ + public static final int CATEGORY_GEO = 0; + + /** + * Alert category related to meteorological (inc. flood). + */ + public static final int CATEGORY_MET = 1; + + /** + * Alert category related to general emergency and public safety. + */ + public static final int CATEGORY_SAFETY = 2; + + /** + * Alert category related to law enforcement, military, homeland and local/private security. + */ + public static final int CATEGORY_SECURITY = 3; + + /** + * Alert category related to rescue and recovery. + */ + public static final int CATEGORY_RESCUE = 4; + + /** + * Alert category related to fire suppression and rescue. + */ + public static final int CATEGORY_FIRE = 5; + + /** + * Alert category related to medical and public health. + */ + public static final int CATEGORY_HEALTH = 6; + + /** + * Alert category related to pollution and other environmental. + */ + public static final int CATEGORY_ENV = 7; + + /** + * Alert category related to public and private transportation. + */ + public static final int CATEGORY_TRANSPORT = 8; + + /** + * Alert category related to utility, telecommunication, other non-transport infrastructure. + */ + public static final int CATEGORY_INFRA = 9; + + /** + * Alert category related to chemical, biological, radiological, nuclear or high-yield + * explosive threat or attack. + */ + public static final int CATEGORY_CBRNE = 10; + + /** + * Alert category of other events. + */ + public static final int CATEGORY_OTHER = 11; + + /** + * The category of the subject event of the emergency alert message. + * + * @hide + */ + @IntDef(prefix = { "CATEGORY_" }, value = { + CATEGORY_GEO, + CATEGORY_MET, + CATEGORY_SAFETY, + CATEGORY_SECURITY, + CATEGORY_RESCUE, + CATEGORY_FIRE, + CATEGORY_HEALTH, + CATEGORY_ENV, + CATEGORY_TRANSPORT, + CATEGORY_INFRA, + CATEGORY_CBRNE, + CATEGORY_OTHER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertCategory {} + + /** + * Urgency indicating that responsive action should be taken immediately. + */ + public static final int URGENCY_IMMEDIATE = 0; + + /** + * Urgency indicating that responsive action should be taken soon. + */ + public static final int URGENCY_EXPECTED = 1; + + /** + * Urgency indicating that responsive action should be taken in the near future. + */ + public static final int URGENCY_FUTURE = 2; + + /** + * Urgency indicating that responsive action is no longer required. + */ + public static final int URGENCY_PAST = 3; + + /** + * Unknown Urgency. + */ + public static final int URGENCY_UNKNOWN = 4; + + /** + * The urgency of the subject event of the emergency alert message. + * + * @hide + */ + @IntDef(prefix = { "URGENCY_" }, value = { + URGENCY_IMMEDIATE, + URGENCY_EXPECTED, + URGENCY_FUTURE, + URGENCY_PAST, + URGENCY_UNKNOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertUrgency {} + + /** + * Severity indicating extraordinary threat to life or property. + */ + public static final int SEVERITY_EXTREME = 0; + + /** + * Severity indicating significant threat to life or property. + */ + public static final int SEVERITY_SEVERE = 1; + + /** + * Severity indicating possible threat to life or property. + */ + public static final int SEVERITY_MODERATE = 2; + + /** + * Severity indicating minimal to no known threat to life or property. + */ + public static final int SEVERITY_MINOR = 3; + + /** + * Unknown severity. + */ + public static final int SEVERITY_UNKNOWN = 4; + + /** + * The severity of the subject event of the emergency alert message. + * + * @hide + */ + @IntDef(prefix = { "SEVERITY_" }, value = { + SEVERITY_EXTREME, + SEVERITY_SEVERE, + SEVERITY_MODERATE, + SEVERITY_MINOR, + SEVERITY_UNKNOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertSeverity {} + + /** + * Certainty indicating that the event is determined to has occurred or to be ongoing. + */ + public static final int CERTAINTY_OBSERVED = 0; + + /** + * Certainty indicating that the event is likely (probability > ~50%). + */ + public static final int CERTAINTY_LIKELY = 1; + + /** + * Certainty indicating that the event is possible but not likely (probability <= ~50%). + */ + public static final int CERTAINTY_POSSIBLE = 2; + + /** + * Certainty indicating that the event is not expected to occur (probability ~ 0). + */ + public static final int CERTAINTY_UNLIKELY = 3; + + /** + * Unknown certainty. + */ + public static final int CERTAINTY_UNKNOWN = 4; + + /** + * The certainty of the subject event of the emergency alert message. + * + * @hide + */ + @IntDef(prefix = { "CERTAINTY_" }, value = { + CERTAINTY_OBSERVED, + CERTAINTY_LIKELY, + CERTAINTY_POSSIBLE, + CERTAINTY_UNLIKELY, + CERTAINTY_UNKNOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AlertCertainty {} + public static final class Geocode implements Parcelable { private final String mValueName; @@ -45,8 +315,9 @@ public final class RadioAlert implements Parcelable { /** * Constructor of geocode. * - * @param valueName Name of geocode value - * @param value Value of geocode + * @param valueName Name of geocode value. + * @param value Value of geocode. + * * @hide */ public Geocode(@NonNull String valueName, @NonNull String value) { @@ -71,6 +342,30 @@ public final class RadioAlert implements Parcelable { } }; + /** + * Gets the value name of a geographic code. + * + * <p>Value name are acronyms should be represented in all capital + * letters without periods (e.g., SAME, FIPS, ZIP). See ITU-T X.1303 + * bis for more info). + * + * @return Value name of a geographic code. + */ + @NonNull + public String getValueName() { + return mValueName; + } + + /** + * Gets the value of a geographic code. + * + * @return Value of a geographic code. + */ + @NonNull + public String getValue() { + return mValue; + } + @Override public int describeContents() { return 0; @@ -116,6 +411,7 @@ public final class RadioAlert implements Parcelable { * * @param latitude Latitude of the coordinate * @param longitude Longitude of the coordinate + * * @hide */ public Coordinate(double latitude, double longitude) { @@ -147,6 +443,26 @@ public final class RadioAlert implements Parcelable { } }; + /** + * Gets the latitude of a coordinate. + * + * @return Latitude of a coordinate. + */ + @FloatRange(from = -90.0, to = 90.0) + public double getLatitude() { + return mLatitude; + } + + /** + * Gets the longitude of a coordinate. + * + * @return Longitude of a coordinate. + */ + @FloatRange(from = -180.0, to = 180.0) + public double getLongitude() { + return mLongitude; + } + @Override public int describeContents() { return 0; @@ -189,6 +505,7 @@ public final class RadioAlert implements Parcelable { * Constructor of polygon. * * @param coordinates Coordinates the polygon is composed of + * * @hide */ public Polygon(@NonNull List<Coordinate> coordinates) { @@ -220,6 +537,19 @@ public final class RadioAlert implements Parcelable { } }; + /** + * Gets the coordinates of points defining a polygon. + * + * <p>A minimum of 4 coordinates must be present and the first and last + * coordinates must be the same. See WGS 84 for more information. + * + * @return Paired values of points defining a polygon. + */ + @NonNull + public List<Coordinate> getCoordinates() { + return mCoordinates; + } + @Override public int describeContents() { return 0; @@ -263,6 +593,7 @@ public final class RadioAlert implements Parcelable { * * @param polygons Polygons used in alert area * @param geocodes Geocodes used in alert area + * * @hide */ public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) { @@ -289,6 +620,28 @@ public final class RadioAlert implements Parcelable { } }; + /** + * Gets polygons delineating the affected area of the alert message. + * + * @return Polygons delineating the affected area of the alert message. + */ + @NonNull + public List<Polygon> getPolygons() { + return mPolygons; + } + + /** + * Gets the geographic codes delineating the affected area of the alert + * message. + * + * @return geographic codes delineating the affected area of the alert + * message. + */ + @NonNull + public List<Geocode> getGeocodes() { + return mGeocodes; + } + @Override public int describeContents() { return 0; @@ -326,18 +679,18 @@ public final class RadioAlert implements Parcelable { public static final class AlertInfo implements Parcelable { - private final List<Integer> mCategoryList; - private final int mUrgency; - private final int mSeverity; - private final int mCertainty; - private final String mTextualMessage; - private final List<AlertArea> mAreaList; + private final @NonNull int[] mCategories; + private final @AlertUrgency int mUrgency; + private final @AlertSeverity int mSeverity; + private final @AlertCertainty int mCertainty; + private final @NonNull String mTextualMessage; + private final @NonNull List<AlertArea> mAreaList; @Nullable private final String mLanguage; /** * Constructor for alert info. * - * @param categoryList Array of categories of the subject event of the alert message + * @param categories Array of categories of the subject event of the alert message * @param urgency The urgency of the subject event of the alert message * @param severity The severity of the subject event of the alert message * @param certainty The certainty of the subject event of the alert message @@ -347,10 +700,13 @@ public final class RadioAlert implements Parcelable { * @param language The optional language field of the alert info * @hide */ - public AlertInfo(@NonNull List<Integer> categoryList, int urgency, - int severity, int certainty, String textualMessage, - @NonNull List<AlertArea> areaList, @Nullable String language) { - mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null"); + public AlertInfo(@NonNull int[] categories, @AlertUrgency int urgency, + @AlertSeverity int severity, @AlertCertainty int certainty, + String textualMessage, @NonNull List<AlertArea> areaList, + @Nullable String language) { + Objects.requireNonNull(categories, "Categories can not be null"); + Arrays.sort(categories); + mCategories = categories; mUrgency = urgency; mSeverity = severity; mCertainty = certainty; @@ -360,7 +716,8 @@ public final class RadioAlert implements Parcelable { } private AlertInfo(Parcel in) { - mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class); + mCategories = new int[in.readInt()]; + in.readIntArray(mCategories); mUrgency = in.readInt(); mSeverity = in.readInt(); mCertainty = in.readInt(); @@ -375,6 +732,84 @@ public final class RadioAlert implements Parcelable { } } + /** + * Gets categories of the subject event of the alert info. + * + * <p>According to ITU-T X.1303, a single alert info block may contains multiple categories. + * + * @return Categories of the subject event of the alert info. + */ + @NonNull + public int[] getCategories() { + return mCategories; + } + + /** + * Gets the urgency of the subject event of the alert info. + * + * <p>Urgency represents the time available to prepare for the alert. See ITU-T X.1303 bis + * for more info. + * + * @return The urgency of the subject event of the alert info. + */ + @AlertUrgency public int getUrgency() { + return mUrgency; + } + + /** + * Gets the severity of the subject event of the alert info. + * + * <p>Severity represents the intensity of impact. See ITU-T X.1303 bis for more info. + * + * @return The urgency of the subject event of the alert info + */ + @AlertSeverity public int getSeverity() { + return mSeverity; + } + + /** + * Gets the certainty of the subject event of the alert info. + * + * <p>Certainty represents confidence in the observation or prediction. See ITU-T X.1303 + * bis for more info. + * + * @return The certainty of the subject event of the alert info. + */ + @AlertCertainty public int getCertainty() { + return mCertainty; + } + + /** + * Gets textual descriptions of the subject event. + * + * @return Textual descriptions of the subject event. + */ + @NonNull + public String getDescription() { + return mTextualMessage; + } + + /** + * Gets geographic areas to which the alert info segment in which it + * appears applies. + * + * @return Areas to which the alert info segment in which it appears applies. + */ + @NonNull + public List<AlertArea> getAreas() { + return mAreaList; + } + + /** + * Gets IETF RFC 3066 language code donating the language of the alert message. + * + * @return {@code null} if unspecified, otherwise IETF RFC 3066 language code + */ + @Nullable + public String getLanguage() { + return mLanguage; + } + public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() { @Override public AlertInfo createFromParcel(Parcel in) { @@ -394,7 +829,8 @@ public final class RadioAlert implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeList(mCategoryList); + dest.writeInt(mCategories.length); + dest.writeIntArray(mCategories); dest.writeInt(mUrgency); dest.writeInt(mSeverity); dest.writeInt(mCertainty); @@ -411,16 +847,16 @@ public final class RadioAlert implements Parcelable { @NonNull @Override public String toString() { - return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency - + ", severity=" + mSeverity + ", certainty=" + mCertainty + return "AlertInfo [categories=" + Arrays.toString(mCategories) + ", urgency=" + + mUrgency + ", severity=" + mSeverity + ", certainty=" + mCertainty + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + ", language=" + mLanguage + "]"; } @Override public int hashCode() { - return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage, - mAreaList, mLanguage); + return Objects.hash(Arrays.hashCode(mCategories), mUrgency, mSeverity, mCertainty, + mTextualMessage, mAreaList, mLanguage); } @Override @@ -432,16 +868,17 @@ public final class RadioAlert implements Parcelable { return false; } - return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency - && mSeverity == other.mSeverity && mCertainty == other.mCertainty + return Arrays.equals(mCategories, other.mCategories) + && mUrgency == other.mUrgency && mSeverity == other.mSeverity + && mCertainty == other.mCertainty && mTextualMessage.equals(other.mTextualMessage) && mAreaList.equals(other.mAreaList) && Objects.equals(mLanguage, other.mLanguage); } } - private final int mStatus; - private final int mMessageType; + private final @AlertStatus int mStatus; + private final @AlertMessageType int mMessageType; private final List<AlertInfo> mInfoList; /** @@ -452,7 +889,7 @@ public final class RadioAlert implements Parcelable { * @param infoList List of alert info * @hide */ - public RadioAlert(int status, int messageType, + public RadioAlert(@AlertStatus int status, @AlertMessageType int messageType, @NonNull List<AlertInfo> infoList) { mStatus = status; mMessageType = messageType; @@ -466,6 +903,42 @@ public final class RadioAlert implements Parcelable { AlertInfo.class); } + /** + * Gets the status of the alert message. + * + * <p>Status is the appropriate handling of the alert message. See ITU-T X.1303 bis for more + * info. + * + * @return The status of the alert message. + */ + @AlertStatus public int getStatus() { + return mStatus; + } + + /** + * Gets the message type of the alert message. + * + * <p>Message type is The nature of the emergency alert message. See ITU-T X.1303 bis for + * more info. + * + * @return The message type of the alert message. + */ + @AlertMessageType public int getMessageType() { + return mMessageType; + } + + /** + * Gets the alert info list. + * + * <p>See ITU-T X.1303 bis for more info of alert info. + * + * @return The alert info list. + */ + @NonNull + public List<AlertInfo> getInfoList() { + return mInfoList; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mStatus); diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 61854e44287b..c6fa9c2f99c1 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -1547,6 +1547,7 @@ public class RadioManager { private final int mSignalQuality; @Nullable private final RadioMetadata mMetadata; @NonNull private final Map<String, String> mVendorInfo; + @Nullable private final RadioAlert mAlert; /** @hide */ public ProgramInfo(@NonNull ProgramSelector selector, @@ -1555,6 +1556,30 @@ public class RadioManager { @Nullable Collection<ProgramSelector.Identifier> relatedContent, int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, @Nullable Map<String, String> vendorInfo) { + this(selector, logicallyTunedTo, physicallyTunedTo, relatedContent, infoFlags, + signalQuality, metadata, vendorInfo, /* alert= */ null); + } + + /** + * Constructor for program info. + * + * @param selector Program selector + * @param logicallyTunedTo Program identifier logically tuned to + * @param physicallyTunedTo Program identifier physically tuned to + * @param relatedContent Related content + * @param infoFlags Program info flags + * @param signalQuality Signal quality + * @param metadata Radio metadata + * @param vendorInfo Vendor parameters + * @param alert Radio alert + * @hide + */ + public ProgramInfo(@NonNull ProgramSelector selector, + @Nullable ProgramSelector.Identifier logicallyTunedTo, + @Nullable ProgramSelector.Identifier physicallyTunedTo, + @Nullable Collection<ProgramSelector.Identifier> relatedContent, + int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, + @Nullable Map<String, String> vendorInfo, @Nullable RadioAlert alert) { mSelector = Objects.requireNonNull(selector); mLogicallyTunedTo = logicallyTunedTo; mPhysicallyTunedTo = physicallyTunedTo; @@ -1568,6 +1593,7 @@ public class RadioManager { mSignalQuality = signalQuality; mMetadata = metadata; mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; + mAlert = alert; } /** @@ -1746,6 +1772,19 @@ public class RadioManager { } /** + * Get alert message. + * + * <p>Alert message can be sent from a radio station of technologies such as HD radio to + * the radio users for some emergency events. + * + * @return alert message if it exists, otherwise {@code null} + */ + @FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + @Nullable public RadioAlert getAlert() { + return mAlert; + } + + /** * Signal quality (as opposed to the name) indication from 0 (no signal) * to 100 (excellent) * @return the signal quality indication. @@ -1786,6 +1825,12 @@ public class RadioManager { mSignalQuality = in.readInt(); mMetadata = in.readTypedObject(RadioMetadata.CREATOR); mVendorInfo = Utils.readStringMap(in); + if (Flags.hdRadioEmergencyAlertSystem()) { + boolean hasNonNullAlert = in.readBoolean(); + mAlert = hasNonNullAlert ? in.readTypedObject(RadioAlert.CREATOR) : null; + } else { + mAlert = null; + } } public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR @@ -1809,6 +1854,14 @@ public class RadioManager { dest.writeInt(mSignalQuality); dest.writeTypedObject(mMetadata, flags); Utils.writeStringMap(dest, mVendorInfo); + if (Flags.hdRadioEmergencyAlertSystem()) { + if (mAlert == null) { + dest.writeBoolean(false); + } else { + dest.writeBoolean(true); + dest.writeTypedObject(mAlert, flags); + } + } } @Override @@ -1819,19 +1872,28 @@ public class RadioManager { @NonNull @Override public String toString() { - return "ProgramInfo" + String prorgamInfoString = "ProgramInfo" + " [selector=" + mSelector + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo) + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo) + ", relatedContent=" + mRelatedContent.size() + ", infoFlags=" + mInfoFlags - + ", mSignalQuality=" + mSignalQuality - + ", mMetadata=" + Objects.toString(mMetadata) - + "]"; + + ", signalQuality=" + mSignalQuality + + ", metadata=" + Objects.toString(mMetadata); + if (Flags.hdRadioEmergencyAlertSystem()) { + prorgamInfoString += ", alert=" + Objects.toString(mAlert); + } + prorgamInfoString += "]"; + return prorgamInfoString; } @Override public int hashCode() { + if (Flags.hdRadioEmergencyAlertSystem()) { + return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo, + mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo, + mAlert); + } return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo, mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo); } @@ -1851,6 +1913,9 @@ public class RadioManager { if (!Objects.equals(mMetadata, other.mMetadata)) return false; if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; + if (Flags.hdRadioEmergencyAlertSystem()) { + return Objects.equals(mAlert, other.mAlert); + } return true; } } diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index a1e7567faead..c6fd0ee3c80d 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -1627,7 +1627,7 @@ public class SoundTrigger { boolean allowMultipleTriggers = in.readBoolean(); KeyphraseRecognitionExtra[] keyphrases = in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); - byte[] data = in.readBlob(); + byte[] data = in.createByteArray(); int audioCapabilities = in.readInt(); return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data, audioCapabilities); @@ -1638,7 +1638,7 @@ public class SoundTrigger { dest.writeBoolean(mCaptureRequested); dest.writeBoolean(mAllowMultipleTriggers); dest.writeTypedArray(mKeyphrases, flags); - dest.writeBlob(mData); + dest.writeByteArray(mData); dest.writeInt(mAudioCapabilities); } diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index e68c4ca5c070..6325b003a999 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -370,7 +370,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { * and most battery stats resets. */ public Builder accumulated() { - mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED; + mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED + | FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE + | FLAG_BATTERY_USAGE_STATS_INCLUDE_SCREEN_STATE + | FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA; return this; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a89483394611..13d7e3c2fbfd 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1274,6 +1274,12 @@ public class Build { * Vanilla Ice Cream. */ public static final int VANILLA_ICE_CREAM = 35; + + /** + * Baklava. + */ + @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) + public static final int BAKLAVA = CUR_DEVELOPMENT; } /** @hide */ @@ -1313,6 +1319,7 @@ public class Build { VERSION_CODES_FULL.TIRAMISU, VERSION_CODES_FULL.UPSIDE_DOWN_CAKE, VERSION_CODES_FULL.VANILLA_ICE_CREAM, + VERSION_CODES_FULL.BAKLAVA, }) @Retention(RetentionPolicy.SOURCE) public @interface SdkIntFull {} @@ -1514,6 +1521,11 @@ public class Build { */ public static final int VANILLA_ICE_CREAM = VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER; + + /** + * The upcoming, not yet finalized, version of Android. + */ + public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER; } /** diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java index 6afb8e097121..8eaadde5a1d0 100644 --- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java +++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java @@ -29,6 +29,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import dalvik.annotation.optimization.NeverCompile; + import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1331,6 +1333,7 @@ public final class MessageQueue { mMatchAllFutureMessages, true); } + @NeverCompile private void printPriorityQueueNodes() { Iterator<MessageNode> iterator = mPriorityQueue.iterator(); @@ -1342,6 +1345,7 @@ public final class MessageQueue { } } + @NeverCompile private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, Printer pw, String prefix, Handler h, int n) { int count = 0; @@ -1357,6 +1361,7 @@ public final class MessageQueue { return count; } + @NeverCompile void dump(Printer pw, String prefix, Handler h) { long now = SystemClock.uptimeMillis(); int n = 0; @@ -1387,6 +1392,7 @@ public final class MessageQueue { + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")"); } + @NeverCompile private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, ProtoOutputStream proto) { int count = 0; @@ -1399,6 +1405,7 @@ public final class MessageQueue { return count; } + @NeverCompile void dumpDebug(ProtoOutputStream proto, long fieldId) { final long messageQueueToken = proto.start(fieldId); diff --git a/core/java/android/os/ITradeInMode.aidl b/core/java/android/os/ITradeInMode.aidl new file mode 100644 index 000000000000..f15954d14d0e --- /dev/null +++ b/core/java/android/os/ITradeInMode.aidl @@ -0,0 +1,62 @@ +/* + * 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.os; + +/** @hide */ +interface ITradeInMode { + /** + * Enable adb in limited-privilege trade-in mode. Returns true if trade-in + * mode was enabled. + * + * Trade-in mode can be enabled if the following conditions are all true: + * ro.debuggable is 0. + * Settings.Global.ADB_ENABLED is 0. + * Settings.Global.USER_SETUP_COMPLETE is 0. + * Settings.Secure.DEVICE_PROVISIONED is 0. + * + * It is stopped automatically when any of the following conditions become + * true: + * + * Settings.Global.USER_SETUP_COMPLETE is 1. + * Settings.Secure.DEVICE_PROVISIONED is 1. + * A change in network configuration occurs. + * An account is added. + * + * ENTER_TRADE_IN_MODE permission is required. + */ + boolean start(); + + /** + * Returns whether evaluation mode is allowed on this device. It will return + * false if any kind of device protection (such as FRP) is detected. + * + * ENTER_TRADE_IN_MODE permission is required. + */ + boolean isEvaluationModeAllowed(); + + /** + * Enable full adb access and provision the device. This forces a factory + * reset on the next boot. + * + * This will return false if start() was not called, if factory reset + * protection is active, or if trade-in mode was disabled due to any of the + * conditions listed above for start(). + * + * ENTER_TRADE_IN_MODE permission is required. + */ + boolean enterEvaluationMode(); +} diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java index 4474e7e91fdc..9f7b0b71ae95 100644 --- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java +++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java @@ -28,6 +28,8 @@ import android.util.Printer; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import dalvik.annotation.optimization.NeverCompile; + import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1106,6 +1108,7 @@ public final class MessageQueue { } } + @NeverCompile void dump(Printer pw, String prefix, Handler h) { synchronized (this) { pw.println(prefix + "(MessageQueue is using Legacy implementation)"); @@ -1122,6 +1125,7 @@ public final class MessageQueue { } } + @NeverCompile void dumpDebug(ProtoOutputStream proto, long fieldId) { final long messageQueueToken = proto.start(fieldId); synchronized (this) { diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java index f1affce58a5c..f3eec13cb20f 100644 --- a/core/java/android/os/LockedMessageQueue/MessageQueue.java +++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java @@ -30,6 +30,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import dalvik.annotation.optimization.NeverCompile; + import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -294,6 +296,7 @@ public final class MessageQueue { * Keep this for manual debugging. It's easier to pepper the code with this function * than MessageQueue.dump() */ + @NeverCompile void print() { Log.v(TAG, "heap num elem: " + mNumElements + " mHeap.length " + mHeap.length); for (int i = 0; i < mNumElements; i++) { @@ -1209,6 +1212,7 @@ public final class MessageQueue { sMatchAllFutureMessages, true); } + @NeverCompile int dumpPriorityQueue(Printer pw, String prefix, Handler h, MessageHeap priorityQueue) { int n = 0; long now = SystemClock.uptimeMillis(); @@ -1222,6 +1226,7 @@ public final class MessageQueue { return n; } + @NeverCompile void dumpPriorityQueue(ProtoOutputStream proto, MessageHeap priorityQueue) { for (int i = 0; i < priorityQueue.numElements(); i++) { Message m = priorityQueue.getMessageAt(i); @@ -1229,6 +1234,7 @@ public final class MessageQueue { } } + @NeverCompile void dump(Printer pw, String prefix, Handler h) { synchronized (this) { pw.println(prefix + "(MessageQueue is using Locked implementation)"); @@ -1240,6 +1246,7 @@ public final class MessageQueue { } } + @NeverCompile void dumpDebug(ProtoOutputStream proto, long fieldId) { final long messageQueueToken = proto.start(fieldId); synchronized (this) { diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java index 023359726f90..db323dcf9009 100644 --- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java +++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java @@ -29,6 +29,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import dalvik.annotation.optimization.NeverCompile; + import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1249,6 +1251,7 @@ public final class MessageQueue { mMatchAllFutureMessages, true); } + @NeverCompile private void printPriorityQueueNodes() { Iterator<MessageNode> iterator = mPriorityQueue.iterator(); @@ -1260,6 +1263,7 @@ public final class MessageQueue { } } + @NeverCompile private int dumpPriorityQueue(PriorityQueue<MessageNode> queue, Printer pw, String prefix, Handler h, int n) { int count = 0; @@ -1275,6 +1279,7 @@ public final class MessageQueue { return count; } + @NeverCompile void dump(Printer pw, String prefix, Handler h) { long now = SystemClock.uptimeMillis(); int n = 0; @@ -1307,6 +1312,7 @@ public final class MessageQueue { + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")"); } + @NeverCompile private int dumpPriorityQueue(PriorityQueue<MessageNode> queue, ProtoOutputStream proto) { int count = 0; @@ -1318,6 +1324,7 @@ public final class MessageQueue { return count; } + @NeverCompile void dumpDebug(ProtoOutputStream proto, long fieldId) { final long messageQueueToken = proto.start(fieldId); diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 9419032c46f8..9dec8673f019 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -316,9 +316,7 @@ public class VibratorInfo implements Parcelable { * @return True if the hardware can control the frequency of the vibrations, otherwise false. */ public boolean hasFrequencyControl() { - // We currently can only control frequency of the vibration using the compose PWLE method. - return hasCapability( - IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + return hasCapability(IVibrator.CAP_FREQUENCY_CONTROL); } /** @@ -481,7 +479,8 @@ public class VibratorInfo implements Parcelable { * @return True if the hardware supports creating envelope effects, false otherwise. */ public boolean areEnvelopeEffectsSupported() { - return hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2); + return hasCapability( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2); } /** diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index c7cc653f4178..9c83bc2c88ec 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -91,6 +91,14 @@ flag { } flag { + name: "allow_thermal_thresholds_callback" + is_exported: true + namespace: "game" + description: "Enable thermal threshold callback" + bug: "360486877" +} + +flag { name: "android_os_build_vanilla_ice_cream" is_exported: true namespace: "build" diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index bfefba5b36e6..1d654e1322c7 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -282,3 +282,28 @@ flag { description: "This fixed read-only flag is used to enable platform support for Skin Temperature." bug: "369872443" } + +flag { + name: "platform_oxygen_saturation_enabled" + is_fixed_read_only: true + is_exported: true + namespace: "android_health_services" + description: "This fixed read-only flag is used to enable platform support for Oxygen Saturation (SpO2)." + bug: "369873227" +} + +flag { + name: "allow_host_permission_dialogs_on_virtual_devices" + is_exported: true + namespace: "permissions" + description: "Allow host device permission dialogs (i.e., dialogs for non device-aware permissions) to be shown on virtual devices" + bug: "371173672" +} + +flag { + name: "appop_mode_caching_enabled" + is_fixed_read_only: true + namespace: "permissions" + description: "Enable AppOp mode caching in AppOpsManager" + bug: "366013082" +} diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl index 8f33e0b2c003..9d384fba874d 100644 --- a/core/java/android/print/IPrintDocumentAdapter.aidl +++ b/core/java/android/print/IPrintDocumentAdapter.aidl @@ -37,5 +37,4 @@ oneway interface IPrintDocumentAdapter { void write(in PageRange[] pages, in ParcelFileDescriptor fd, IWriteResultCallback callback, int sequence); void finish(); - void kill(String reason); } diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index ef274a56e1d3..1b1554f8192d 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -946,17 +946,6 @@ public final class PrintManager { } @Override - public void kill(String reason) { - synchronized (mLock) { - // If destroyed the handler is null. - if (!isDestroyedLocked()) { - mHandler.obtainMessage(MyHandler.MSG_ON_KILL, - reason).sendToTarget(); - } - } - } - - @Override public void onActivityPaused(Activity activity) { /* do nothing */ } @@ -1118,15 +1107,6 @@ public final class PrintManager { } } break; - case MSG_ON_KILL: { - if (DEBUG) { - Log.i(LOG_TAG, "onKill()"); - } - - String reason = (String) message.obj; - throw new RuntimeException(reason); - } - default: { throw new IllegalArgumentException("Unknown message: " + message.what); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 5ecf361e83c7..8afc1779ed00 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3071,6 +3071,14 @@ public final class ContactsContract { "queryDefaultAccountForNewContacts"; /** + * Action used to launch the UI to move contacts to the default account. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MOVE_CONTACTS_TO_DEFAULT_ACCOUNT = + "android.provider.action.MOVE_CONTACTS_TO_DEFAULT_ACCOUNT"; + + + /** * Represents the state of the default account, and the actual {@link Account} if it's * a cloud account. * If the default account is set to {@link #DEFAULT_ACCOUNT_STATE_LOCAL} or diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 764570ead4e1..83c599e57de9 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5472,6 +5472,14 @@ public final class Settings { public static final String VOLUME_MASTER = "volume_master"; /** + * The mapping of input device to its input gain index. + * + * @hide + */ + @Readable + public static final String INPUT_GAIN_INDEX_SETTINGS = "input_gain_index_settings"; + + /** * Master mono (int 1 = mono, 0 = normal). * * @hide @@ -8390,7 +8398,6 @@ public final class Settings { @Readable public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout"; - /** * This preference contains the string that shows for owner info on LockScreen. * @hide @@ -11281,7 +11288,8 @@ public final class Settings { "assist_long_press_home_enabled"; /** - * Whether all entrypoints can trigger search. Replaces individual settings. + * Whether all entrypoints (e.g. long-press home, long-press nav handle) + * can trigger contextual search. * * @hide */ diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java index 9cbc5ecea962..90906edcc636 100644 --- a/core/java/android/security/forensic/ForensicEvent.java +++ b/core/java/android/security/forensic/ForensicEvent.java @@ -61,6 +61,14 @@ public final class ForensicEvent implements Parcelable { in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class); } + public String getType() { + return mType; + } + + public Map<String, String> getKeyValuePairs() { + return mKeyValuePairs; + } + @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeString(mType); diff --git a/core/java/android/service/settings/OWNERS b/core/java/android/service/settings/OWNERS new file mode 100644 index 000000000000..c70c7380ec0e --- /dev/null +++ b/core/java/android/service/settings/OWNERS @@ -0,0 +1,7 @@ +# Bug component: 27091 + +cantol@google.com +cechkahn@google.com +cipson@google.com +dswliu@google.com +jiannan@google.com diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e8ef9d65a2b4..bce51f297aff 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1701,6 +1701,11 @@ public class PhoneStateListener { public final void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) { // not supported on the deprecated interface - Use TelephonyCallback instead } + + public final void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType int[] availableServices) { + // not supported on the deprecated interface - Use TelephonyCallback instead + } } private void log(String s) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 5295b606dd19..46e27dc60adc 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -681,6 +681,20 @@ public class TelephonyCallback { public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43; /** + * Event for listening to changes in carrier roaming non-terrestrial network available services + * via callback onCarrierRoamingNtnAvailableServicesChanged(). + * This callback is triggered when the available services provided by the carrier roaming + * satellite changes. The carrier roaming satellite is defined by the following conditions. + * <ul> + * <li>Subscription supports attaching to satellite which is defined by + * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li> + * </ul> + * + * @hide + */ + public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44; + + /** * @hide */ @IntDef(prefix = {"EVENT_"}, value = { @@ -726,7 +740,8 @@ public class TelephonyCallback { EVENT_EMERGENCY_CALLBACK_MODE_CHANGED, EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED, EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED, - EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED + EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED, + EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface TelephonyEvent { @@ -1784,6 +1799,15 @@ public class TelephonyCallback { * </ul> */ default void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {} + + /** + * Callback invoked when carrier roaming non-terrestrial network available + * service changes. + * + * @param availableServices The list of the supported services. + */ + default void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {} } /** @@ -2235,5 +2259,19 @@ public class TelephonyCallback { Binder.withCleanCallingIdentity(() -> mExecutor.execute( () -> listener.onCarrierRoamingNtnEligibleStateChanged(eligible))); } + + public void onCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType int[] availableServices) { + if (!Flags.carrierRoamingNbIotNtn()) return; + + CarrierRoamingNtnModeListener listener = + (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + List<Integer> ServiceList = Arrays.stream(availableServices).boxed() + .collect(Collectors.toList()); + Binder.withCleanCallingIdentity(() -> mExecutor.execute( + () -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList))); + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3c7e924f07df..4d50a450490e 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -1118,6 +1118,21 @@ public class TelephonyRegistryManager { } /** + * Notify external listeners that carrier roaming non-terrestrial available services changed. + * @param availableServices The list of the supported services. + * @hide + */ + public void notifyCarrierRoamingNtnAvailableServicesChanged( + int subId, @NetworkRegistrationInfo.ServiceType int[] availableServices) { + try { + sRegistry.notifyCarrierRoamingNtnAvailableServicesChanged(subId, availableServices); + } catch (RemoteException ex) { + // system server crash + throw ex.rethrowFromSystemServer(); + } + } + + /** * Processes potential event changes from the provided {@link TelephonyCallback}. * * @param telephonyCallback callback for monitoring callback changes to the telephony state. @@ -1272,12 +1287,9 @@ public class TelephonyRegistryManager { if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) { eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED); - } - - if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) { eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED); + eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED); } - return eventList; } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 396be7b62b4d..03f9d9814b43 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -18,9 +18,11 @@ package android.view; import static android.system.OsConstants.EINVAL; +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo.Translator; @@ -1026,6 +1028,211 @@ public class Surface implements Parcelable { } /** + * Parameter object for {@link #setFrameRate(FrameRateParams)}, describing the intended frame + * rate for the Surface that setFrameRate is called on. + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static class FrameRateParams { + private FrameRateParams() {} + + /** + * A static FrameRateParams that can be passed directly into {@link + * #setFrameRate(FrameRateParams)} to indicate the surface has no preference and any frame + * rate is acceptable. + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static final FrameRateParams IGNORE = + new FrameRateParams.Builder().setDesiredRateRange(0f, Float.MAX_VALUE).build(); + + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public static final class Builder { + private float mDesiredMinRate; + private float mDesiredMaxRate; + private float mFixedSourceRate; + private int mChangeFrameRateStrategy; + + /** + * Sets the desired frame rate range (inclusive) values for the surface, specifying that + * the surface prefers the device render rate to be in the range [desiredMinRate, + * desiredMaxRate]. + + * Set desiredMaxRate to FLOAT.MAX_VALUE to indicate the surface prefers any value + * greater than or equal to desiredMinRate. + * + * Set desiredMinRate = desiredMaxRate to indicate the surface prefers an exact frame + * rate. Note that this is different than specifying the fixed source frame rate with + * {@link FrameRateParams.Builder#setFixedSourceRate}. To reiterate, this call is used + * to specify the surface's frame rate preference to be within the desired range. + * + * desiredMaxRate must be greater than or equal to desiredMinRate. + * The values should be greater than or equal to 0. + * + * If the surface has no preference and any frame rate is acceptable, use the constant + * {@link FrameRateParams.IGNORE} in {@link #setFrameRate(FrameRateParams)} instead of + * building {@link FrameRateParams.Builder}. + * + * @see FrameRateParams#getDesiredMinRate() + * @see FrameRateParams#getDesiredMaxRate() + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setDesiredRateRange(@FloatRange(from = 0.0) float desiredMinRate, + @FloatRange(from = 0.0) float desiredMaxRate) { + if (desiredMaxRate < desiredMinRate) { + Log.e(TAG, + "Failed to set desired frame rate range. desiredMaxRate should be " + + "greater than or equal to desiredMinRate"); + return this; + } + mDesiredMinRate = desiredMinRate; + mDesiredMaxRate = desiredMaxRate; + return this; + } + + /** + * Sets the fixed frame rate of the surface when its content has a fixed frame rate, + * e.g. a video with a fixed frame rate. + * + * When the frame rate chosen for the surface is the {@code fixedSourceRate} or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video ({@code fixedSourceRate=30}) renders just as well on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * This method to set the fixed source rate can also be used together with a desired + * frame rate range via {@link FrameRateParams.Builder#setDesiredRateRange}. This still + * means the surface's content has a fixed frame rate of the provided {@code + * fixedSourceRate}, as well as it preferring to be within the desired frame rate range. + * For example, a 30 fps video {@code fixedSourceRate=30} and desired frame rate range + * [60,90] means the surface ideally prefers 60 fps (which is 30 fps * 2) or 90 fps (30 + * fps * 3). + * + * @see FrameRateParams#getFixedSourceRate() + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setFixedSourceRate(@FloatRange(from = 0.0) float fixedSourceRate) { + mFixedSourceRate = fixedSourceRate; + return this; + } + + /** + * Whether display refresh rate transitions caused by this surface should be seamless. A + * seamless transition is one that doesn't have any visual interruptions, such as a + * black screen for a second or two. Value is + * Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, or Surface.CHANGE_FRAME_RATE_ALWAYS + * + * @see FrameRateParams#getChangeFrameRateStrategy() + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public Builder setChangeFrameRateStrategy( + @ChangeFrameRateStrategy int changeFrameRateStrategy) { + mChangeFrameRateStrategy = changeFrameRateStrategy; + return this; + } + + /** + * Builds the FrameRateParams object. + */ + @NonNull + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public FrameRateParams build() { + FrameRateParams frameRate = new FrameRateParams(); + frameRate.mDesiredMinRate = this.mDesiredMinRate; + frameRate.mDesiredMaxRate = this.mDesiredMaxRate; + frameRate.mFixedSourceRate = this.mFixedSourceRate; + frameRate.mChangeFrameRateStrategy = this.mChangeFrameRateStrategy; + return frameRate; + } + } + + /** + * Gets the minimum desired frame rate. + * @see FrameRateParams.Builder#setDesiredRateRange() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getDesiredMinRate() { + return mDesiredMinRate; + } + + /** + * Gets the maximum desired frame rate. + * @see FrameRateParams.Builder#setDesiredRateRange() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getDesiredMaxRate() { + return mDesiredMaxRate; + } + + /** + * Gets the fixed source frame rate. + * @see FrameRateParams.Builder#setFixedSourceRate() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public float getFixedSourceRate() { + return mFixedSourceRate; + } + + /** + * Gets the strategy when changing frame rate. + * @see FrameRateParams.Builder#setChangeFrameRateStrategy + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + @ChangeFrameRateStrategy + public int getChangeFrameRateStrategy() { + return mChangeFrameRateStrategy; + } + + float mDesiredMinRate; + float mDesiredMaxRate; + float mFixedSourceRate; + int mChangeFrameRateStrategy; + } + + /** + * Sets the intended frame rate for this surface. + * + * <p>On devices that are capable of running the display at different frame rates, + * the system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application.</p> + * + * <p>Note that this only has an effect for surfaces presented on the display. If this + * surface is consumed by something other than the system compositor, e.g. a media + * codec, this call has no effect.</p> + * + * @param frameRateParams The parameters describing the intended frame rate of this surface. + * Refer to {@link FrameRateParams} for details. + * @throws IllegalArgumentException If <code>frameRateParams</code> is invalid. + * @see #clearFrameRate() + */ + @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API) + public void setFrameRate(@NonNull FrameRateParams frameRateParams) { + synchronized (mLock) { + checkNotReleasedLocked(); + // TODO(b/362798998): Logic currently incomplete: it uses fixed source rate over the + // desired min/max rates. Fix when plumbing is upgraded. + int compatibility = frameRateParams.getFixedSourceRate() == 0 + ? FRAME_RATE_COMPATIBILITY_DEFAULT + : FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; + float frameRate = compatibility == FRAME_RATE_COMPATIBILITY_DEFAULT + ? frameRateParams.getDesiredMinRate() + : frameRateParams.getFixedSourceRate(); + int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility, + frameRateParams.getChangeFrameRateStrategy()); + if (error == -EINVAL) { + throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()"); + } else if (error != 0) { + Log.e(TAG, "Failed to set frame rate on Surface. Native error: " + error); + } + } + } + + /** * Sets the intended frame rate for this surface. * * <p>On devices that are capable of running the display at different refresh rates, diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index e6de478e3d3d..94f415b8680f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -412,41 +412,28 @@ public final class SurfaceControl implements Parcelable { */ public static class JankData { - /** @hide */ - @IntDef(flag = true, value = {JANK_NONE, - DISPLAY_HAL, - JANK_SURFACEFLINGER_DEADLINE_MISSED, - JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED, - JANK_APP_DEADLINE_MISSED, - PREDICTION_ERROR, - SURFACE_FLINGER_SCHEDULING}) + /** + * Needs to be kept in sync with android_view_SurfaceControl.cpp's + * JankDataListenerWrapper::onJankDataAvailable. + * @hide + */ + @IntDef(flag = true, value = { + JANK_NONE, + JANK_COMPOSER, + JANK_APPLICATION, + JANK_OTHER, + }) @Retention(RetentionPolicy.SOURCE) public @interface JankType {} - // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h - // No Jank - public static final int JANK_NONE = 0x0; - - // Jank not related to SurfaceFlinger or the App - public static final int DISPLAY_HAL = 0x1; - // SF took too long on the CPU - public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2; - // SF took too long on the GPU - public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4; - // Either App or GPU took too long on the frame - public static final int JANK_APP_DEADLINE_MISSED = 0x8; - // Vsync predictions have drifted beyond the threshold from the actual HWVsync - public static final int PREDICTION_ERROR = 0x10; - // Latching a buffer early might cause an early present of the frame - public static final int SURFACE_FLINGER_SCHEDULING = 0x20; - // A buffer is said to be stuffed if it was expected to be presented on a vsync but was - // presented later because the previous buffer was presented in its expected vsync. This - // usually happens if there is an unexpectedly long frame causing the rest of the buffers - // to enter a stuffed state. - public static final int BUFFER_STUFFING = 0x40; - // Jank due to unknown reasons. - public static final int UNKNOWN = 0x80; + public static final int JANK_NONE = 0; + // Jank caused by the composer missing a deadline + public static final int JANK_COMPOSER = 1 << 0; + // Jank caused by the application missing the composer's deadline + public static final int JANK_APPLICATION = 1 << 1; + // Jank due to other unknown reasons + public static final int JANK_OTHER = 1 << 2; public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs, long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) { diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index d90455ab971d..2ca62a0725df 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -220,6 +220,7 @@ public interface ImeTracker { PHASE_WM_POSTING_CHANGED_IME_VISIBILITY, PHASE_WM_INVOKING_IME_REQUESTED_LISTENER, PHASE_CLIENT_ALREADY_HIDDEN, + PHASE_CLIENT_VIEW_HANDLER_AVAILABLE, }) @Retention(RetentionPolicy.SOURCE) @interface Phase {} @@ -424,6 +425,11 @@ public interface ImeTracker { ImeProtoEnums.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER; /** IME is requested to be hidden, but already hidden. Don't hide to avoid another animation. */ int PHASE_CLIENT_ALREADY_HIDDEN = ImeProtoEnums.PHASE_CLIENT_ALREADY_HIDDEN; + /** + * The view's handler is needed to check if we're running on a different thread. We can't + * continue without. + */ + int PHASE_CLIENT_VIEW_HANDLER_AVAILABLE = ImeProtoEnums.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE; /** * Called when an IME request is started. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 47fc43735c4d..2d2f4c958a73 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2557,21 +2557,24 @@ public final class InputMethodManager { public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, ResultReceiver resultReceiver) { return hideSoftInputFromWindow(windowToken, flags, resultReceiver, - SoftInputShowHideReason.HIDE_SOFT_INPUT); + SoftInputShowHideReason.HIDE_SOFT_INPUT, null); } private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, - ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, + @Nullable ImeTracker.Token statsToken) { // Get served view initially for statsToken creation. final View initialServedView; synchronized (mH) { initialServedView = getServedViewLocked(); } - final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, - ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(initialServedView)); - ImeTracker.forLatency().onRequestHide(statsToken, - ImeTracker.ORIGIN_CLIENT, reason, ActivityThread::currentApplication); + if (statsToken == null) { + statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, + ImeTracker.ORIGIN_CLIENT, reason, ImeTracker.isFromUser(initialServedView)); + ImeTracker.forLatency().onRequestHide(statsToken, ImeTracker.ORIGIN_CLIENT, reason, + ActivityThread::currentApplication); + } ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", this, null /* icProto */); checkFocus(); @@ -2591,6 +2594,17 @@ public final class InputMethodManager { // TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY final var viewRootImpl = servedView.getViewRootImpl(); if (viewRootImpl != null) { + Handler vh = servedView.getHandler(); + if (vh == null) { + // If the view doesn't have a handler, something has changed out from + // under us. The current input has been closed before (from checkFocus). + ImeTracker.forLogging().onFailed(statsToken, + ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE); + return false; + } + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE); + if (resultReceiver != null) { final boolean imeReqVisible = (viewRootImpl.getInsetsController().getRequestedVisibleTypes() @@ -2599,7 +2613,15 @@ public final class InputMethodManager { !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN : InputMethodManager.RESULT_HIDDEN, null); } - viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime()); + if (vh.getLooper() != Looper.myLooper()) { + // The view is running on a different thread than our own, so + // we need to reschedule our work for over there. + if (DEBUG) Log.v(TAG, "Hiding soft input: reschedule to view thread"); + vh.post(() -> viewRootImpl.getInsetsController().hide( + WindowInsets.Type.ime())); + } else { + viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime()); + } } return true; } else { @@ -2646,8 +2668,14 @@ public final class InputMethodManager { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(), - statsToken, flags, null, reason, mAsyncShowHideMethodEnabled); + if (Flags.refactorInsetsController()) { + return hideSoftInputFromWindow(view.getWindowToken(), flags, + null /* resultReceiver */, reason, statsToken); + } else { + return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, + view.getWindowToken(), statsToken, flags, null, reason, + mAsyncShowHideMethodEnabled); + } } } @@ -3143,7 +3171,7 @@ public final class InputMethodManager { if (rootInsets != null && rootInsets.isVisible(WindowInsets.Type.ime())) { hideSoftInputFromWindow(view.getWindowToken(), hideFlags, null /* resultReceiver */, - SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT); + SoftInputShowHideReason.HIDE_TOGGLE_SOFT_INPUT, null); } else { showSoftInput(view, showFlags, null /* resultReceiver */, SoftInputShowHideReason.SHOW_TOGGLE_SOFT_INPUT); @@ -3721,14 +3749,19 @@ public final class InputMethodManager { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); - IInputMethodManagerGlobalInvoker.hideSoftInput( - mClient, - rootView.getWindowToken(), - statsToken, - HIDE_NOT_ALWAYS, - null, - reason, - true /*async */); + if (Flags.refactorInsetsController()) { + mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(), + false /* fromIme */, statsToken); + } else { + IInputMethodManagerGlobalInvoker.hideSoftInput( + mClient, + rootView.getWindowToken(), + statsToken, + HIDE_NOT_ALWAYS, + null, + reason, + true /*async */); + } } } diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index 90fac361c966..89b38d8048e5 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -23,7 +23,6 @@ import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; -import android.util.TimeUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -58,7 +57,7 @@ public final class BackEvent { private final float mTouchX; private final float mTouchY; private final float mProgress; - private final long mFrameTime; + private final long mFrameTimeMillis; @SwipeEdge private final int mSwipeEdge; @@ -68,7 +67,7 @@ public final class BackEvent { if (predictiveBackTimestampApi()) { return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge(), - backMotionEvent.getFrameTime()); + backMotionEvent.getFrameTimeMillis()); } else { return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge()); @@ -76,7 +75,7 @@ public final class BackEvent { } /** - * Creates a new {@link BackEvent} instance with the current uptime as frame time. + * Creates a new {@link BackEvent} instance with a frame time of 0. * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. @@ -88,7 +87,7 @@ public final class BackEvent { mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; - mFrameTime = System.nanoTime() / TimeUtils.NANOS_PER_MS; + mFrameTimeMillis = 0; } /** @@ -98,16 +97,16 @@ public final class BackEvent { * @param touchY Absolute Y location of the touch point of this event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param swipeEdge Indicates which edge the swipe starts from. - * @param frameTime frame time of the back event. + * @param frameTimeMillis frame time of the back event. */ @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge, - long frameTime) { + long frameTimeMillis) { mTouchX = touchX; mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; - mFrameTime = frameTime; + mFrameTimeMillis = frameTimeMillis; } /** @@ -160,8 +159,8 @@ public final class BackEvent { * Returns the frameTime of the BackEvent in milliseconds. Useful for calculating velocity. */ @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) - public long getFrameTime() { - return mFrameTime; + public long getFrameTimeMillis() { + return mFrameTimeMillis; } @Override @@ -177,7 +176,7 @@ public final class BackEvent { && mTouchY == that.mTouchY && mProgress == that.mProgress && mSwipeEdge == that.mSwipeEdge - && mFrameTime == that.mFrameTime; + && mFrameTimeMillis == that.mFrameTimeMillis; } @Override @@ -187,7 +186,7 @@ public final class BackEvent { + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress + ", mSwipeEdge=" + mSwipeEdge - + ", mFrameTime=" + mFrameTime + "ms" + + ", mFrameTimeMillis=" + mFrameTimeMillis + "}"; } } diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index a8ec4eeb039a..cc2afbc6aaa3 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -33,7 +33,7 @@ import android.view.RemoteAnimationTarget; public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; - private final long mFrameTime; + private final long mFrameTimeMillis; private final float mProgress; private final boolean mTriggerBack; @@ -49,7 +49,7 @@ public final class BackMotionEvent implements Parcelable { * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. - * @param frameTime Event time of the corresponding touch event. + * @param frameTimeMillis Event time of the corresponding touch event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. @@ -59,14 +59,14 @@ public final class BackMotionEvent implements Parcelable { public BackMotionEvent( float touchX, float touchY, - long frameTime, + long frameTimeMillis, float progress, boolean triggerBack, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; - mFrameTime = frameTime; + mFrameTimeMillis = frameTimeMillis; mProgress = progress; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; @@ -80,7 +80,7 @@ public final class BackMotionEvent implements Parcelable { mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); - mFrameTime = in.readLong(); + mFrameTimeMillis = in.readLong(); } @NonNull @@ -109,7 +109,7 @@ public final class BackMotionEvent implements Parcelable { dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); - dest.writeLong(mFrameTime); + dest.writeLong(mFrameTimeMillis); } /** @@ -156,8 +156,8 @@ public final class BackMotionEvent implements Parcelable { /** * Returns the frame time of the BackMotionEvent in milliseconds. */ - public long getFrameTime() { - return mFrameTime; + public long getFrameTimeMillis() { + return mFrameTimeMillis; } /** @@ -175,7 +175,7 @@ public final class BackMotionEvent implements Parcelable { return "BackMotionEvent{" + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY - + ", mFrameTime=" + mFrameTime + "ms" + + ", mFrameTimeMillis=" + mFrameTimeMillis + ", mProgress=" + mProgress + ", mTriggerBack=" + mTriggerBack + ", mSwipeEdge=" + mSwipeEdge diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index 39a30253adbd..4908068d51e4 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -151,7 +151,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, - /* frameTime = */ 0, + /* frameTimeMillis = */ 0, /* progress = */ 0, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, @@ -236,7 +236,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mLatestTouchX, /* touchY = */ mLatestTouchY, - /* frameTime = */ 0, + /* frameTimeMillis = */ 0, /* progress = */ progress, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 8db1f9509757..bd01899a649b 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -238,7 +238,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { long frameTime = 0; if (predictiveBackTimestampApi()) { - frameTime = backEvent.getFrameTime(); + frameTime = backEvent.getFrameTimeMillis(); } mIOnBackInvokedCallback.onBackStarted( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, @@ -254,7 +254,7 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc try { long frameTime = 0; if (predictiveBackTimestampApi()) { - frameTime = backEvent.getFrameTime(); + frameTime = backEvent.getFrameTimeMillis(); } mIOnBackInvokedCallback.onBackProgressed( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index 235ba3a41427..7ad14b0c9fe8 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -142,4 +142,12 @@ flag { description: "Whether the declarative compat UI framework is enabled" bug: "270361630" is_fixed_read_only: true +} + +flag { + name: "vdm_force_app_universal_resizable_api" + namespace: "large_screen_experiences_app_compat" + description: "Whether the API for forcing apps to be universal resizable on virtual display is available" + bug: "372848702" + is_exported: true }
\ No newline at end of file diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 0bcc9c1f58c5..c4a9e5722369 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -348,3 +348,24 @@ flag { description: "Enables a switch to change the concequence of dragging a window to the top edge." bug: "372614715" } + +flag { + name: "enable_task_resizing_keyboard_shortcuts" + namespace: "lse_desktop_experience" + description: "Enables keyboard shortcuts for resizing tasks in desktop mode." + bug: "335819608" +} + +flag { + name: "enable_display_windowing_mode_switching" + namespace: "lse_desktop_experience" + description: "Change the default display's windowing mode to freeform when display connected in extended mode." + bug: "374849026" +} + +flag { + name: "enable_desktop_windowing_pip" + namespace: "lse_desktop_experience" + description: "Enables PiP features in desktop mode." + bug: "350475854" +} diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig index cd31850b281c..57aacffd4ffc 100644 --- a/core/java/android/window/flags/responsible_apis.aconfig +++ b/core/java/android/window/flags/responsible_apis.aconfig @@ -72,6 +72,13 @@ flag { } flag { + name: "bal_strict_mode" + namespace: "responsible_apis" + description: "Strict mode flag" + bug: "324089586" +} + +flag { name: "bal_reduce_grace_period" namespace: "responsible_apis" description: "Changes to reduce or ideally remove the grace period exemption." diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 6448f10f01b9..0af4bea70e65 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -16,13 +16,9 @@ package com.android.internal.jank; -import static android.view.SurfaceControl.JankData.DISPLAY_HAL; -import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED; +import static android.view.SurfaceControl.JankData.JANK_APPLICATION; +import static android.view.SurfaceControl.JankData.JANK_COMPOSER; import static android.view.SurfaceControl.JankData.JANK_NONE; -import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED; -import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED; -import static android.view.SurfaceControl.JankData.PREDICTION_ERROR; -import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING; import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE; import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE; @@ -181,23 +177,11 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai case JANK_NONE: str.append("JANK_NONE"); break; - case JANK_APP_DEADLINE_MISSED: - str.append("JANK_APP_DEADLINE_MISSED"); + case JANK_APPLICATION: + str.append("JANK_APPLICATION"); break; - case JANK_SURFACEFLINGER_DEADLINE_MISSED: - str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED"); - break; - case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED: - str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED"); - break; - case DISPLAY_HAL: - str.append("DISPLAY_HAL"); - break; - case PREDICTION_ERROR: - str.append("PREDICTION_ERROR"); - break; - case SURFACE_FLINGER_SCHEDULING: - str.append("SURFACE_FLINGER_SCHEDULING"); + case JANK_COMPOSER: + str.append("JANK_COMPOSER"); break; default: str.append("UNKNOWN: ").append(jankType); @@ -230,7 +214,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai mRendererWrapper = mSurfaceOnly ? null : renderer; mMetricsWrapper = mSurfaceOnly ? null : metrics; mViewRoot = mSurfaceOnly ? null : viewRootWrapper; - mObserver = mSurfaceOnly + mObserver = mSurfaceOnly || (Flags.useSfFrameDuration() && Flags.ignoreHwuiIsFirstFrame()) ? null : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), mHandler, /* waitForPresentTime= */ false); @@ -253,6 +237,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() { @Override public void surfaceCreated(SurfaceControl.Transaction t) { + Trace.beginSection("FrameTracker#surfaceCreated"); mHandler.runWithScissors(() -> { if (mSurfaceControl == null) { mSurfaceControl = mViewRoot.getSurfaceControl(); @@ -262,6 +247,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } } }, EXECUTOR_TASK_TIMEOUT); + Trace.endSection(); } @Override @@ -464,23 +450,28 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai @Override public void onJankDataAvailable(SurfaceControl.JankData[] jankData) { postCallback(() -> { - if (mCancelled || mMetricsFinalized) { - return; - } - - for (SurfaceControl.JankData jankStat : jankData) { - if (!isInRange(jankStat.frameVsyncId)) { - continue; + try { + Trace.beginSection("FrameTracker#onJankDataAvailable"); + if (mCancelled || mMetricsFinalized) { + return; } - JankInfo info = findJankInfo(jankStat.frameVsyncId); - if (info != null) { - info.update(jankStat); - } else { - mJankInfos.put((int) jankStat.frameVsyncId, - JankInfo.createFromSurfaceControlCallback(jankStat)); + + for (SurfaceControl.JankData jankStat : jankData) { + if (!isInRange(jankStat.frameVsyncId)) { + continue; + } + JankInfo info = findJankInfo(jankStat.frameVsyncId); + if (info != null) { + info.update(jankStat); + } else { + mJankInfos.put((int) jankStat.frameVsyncId, + JankInfo.createFromSurfaceControlCallback(jankStat)); + } } + processJankInfos(); + } finally { + Trace.endSection(); } - processJankInfos(); }); } @@ -507,29 +498,35 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { postCallback(() -> { - if (mCancelled || mMetricsFinalized) { - return; - } - - // Since this callback might come a little bit late after the end() call. - // We should keep tracking the begin / end timestamp that we can compare with - // vsync timestamp to check if the frame is in the duration of the CUJ. - long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION); - boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1; - long frameVsyncId = - mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID]; + try { + Trace.beginSection("FrameTracker#onFrameMetricsAvailable"); + if (mCancelled || mMetricsFinalized) { + return; + } - if (!isInRange(frameVsyncId)) { - return; - } - JankInfo info = findJankInfo(frameVsyncId); - if (info != null) { - info.update(totalDurationNanos, isFirstFrame); - } else { - mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback( - frameVsyncId, totalDurationNanos, isFirstFrame)); + // Since this callback might come a little bit late after the end() call. + // We should keep tracking the begin / end timestamp that we can compare with + // vsync timestamp to check if the frame is in the duration of the CUJ. + long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION); + boolean isFirstFrame = + mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1; + long frameVsyncId = + mMetricsWrapper.getTiming()[FrameMetrics.Index.FRAME_TIMELINE_VSYNC_ID]; + + if (!isInRange(frameVsyncId)) { + return; + } + JankInfo info = findJankInfo(frameVsyncId); + if (info != null) { + info.update(totalDurationNanos, isFirstFrame); + } else { + mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback( + frameVsyncId, totalDurationNanos, isFirstFrame)); + } + processJankInfos(); + } finally { + Trace.endSection(); } - processJankInfos(); }); } @@ -568,13 +565,20 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } private boolean callbacksReceived(JankInfo info) { - return mSurfaceOnly + return mObserver == null ? info.surfaceControlCallbackFired : info.hwuiCallbackFired && info.surfaceControlCallbackFired; } @UiThread private void finish() { + Trace.beginSection("FrameTracker#finish"); + finishTraced(); + Trace.endSection(); + } + + @UiThread + private void finishTraced() { if (mMetricsFinalized || mCancelled) return; mMetricsFinalized = true; @@ -599,7 +603,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai for (int i = 0; i < mJankInfos.size(); i++) { JankInfo info = mJankInfos.valueAt(i); final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame; - if (isFirstDrawn) { + if (isFirstDrawn && !Flags.ignoreHwuiIsFirstFrame()) { continue; } if (info.frameVsyncId > mEndVsyncId) { @@ -608,16 +612,12 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai if (info.surfaceControlCallbackFired) { totalFramesCount++; boolean missedFrame = false; - if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) { + if ((info.jankType & JANK_APPLICATION) != 0) { Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name); missedAppFramesCount++; missedFrame = true; } - if ((info.jankType & DISPLAY_HAL) != 0 - || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0 - || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0 - || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0 - || (info.jankType & PREDICTION_ERROR) != 0) { + if ((info.jankType & JANK_COMPOSER) != 0) { Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name); missedSfFramesCount++; missedFrame = true; @@ -636,7 +636,7 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } // TODO (b/174755489): Early latch currently gets fired way too often, so we have // to ignore it for now. - if (!mSurfaceOnly && !info.hwuiCallbackFired) { + if (mObserver != null && !info.hwuiCallbackFired) { markEvent("FT#MissedHWUICallback", info.frameVsyncId); Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId + ", CUJ=" + name); @@ -762,7 +762,9 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai * @param observer observer */ public void addObserver(HardwareRendererObserver observer) { - mRenderer.addObserver(observer); + if (observer != null) { + mRenderer.addObserver(observer); + } } /** @@ -770,7 +772,9 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai * @param observer observer */ public void removeObserver(HardwareRendererObserver observer) { - mRenderer.removeObserver(observer); + if (observer != null) { + mRenderer.removeObserver(observer); + } } } diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig index 82f50ae848b3..287ad1856258 100644 --- a/core/java/com/android/internal/jank/flags.aconfig +++ b/core/java/com/android/internal/jank/flags.aconfig @@ -8,3 +8,10 @@ flag { bug: "354763298" is_fixed_read_only: true } +flag { + name: "ignore_hwui_is_first_frame" + namespace: "window_surfaces" + description: "Whether to remove the usage of the HWUI 'is first frame' flag to ignore jank" + bug: "354763298" + is_fixed_read_only: true +} diff --git a/core/java/com/android/internal/os/ApplicationSharedMemory.java b/core/java/com/android/internal/os/ApplicationSharedMemory.java index 84f713edcc1a..e6ea29e483f1 100644 --- a/core/java/com/android/internal/os/ApplicationSharedMemory.java +++ b/core/java/com/android/internal/os/ApplicationSharedMemory.java @@ -21,6 +21,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.CriticalNative; +import dalvik.annotation.optimization.FastNative; import libcore.io.IoUtils; @@ -293,4 +294,34 @@ public class ApplicationSharedMemory implements AutoCloseable { throw new IllegalStateException("Not mutable"); } } + + /** + * Return true if the memory has been mapped. This never throws. + */ + public boolean isMapped() { + return mPtr != 0; + } + + /** + * Return true if the memory is mapped and mutable. This never throws. Note that it returns + * false if the memory is not mapped. + */ + public boolean isMutable() { + return isMapped() && mMutable; + } + + /** + * Provide access to the nonce block needed by {@link PropertyInvalidatedCache}. This method + * returns 0 if the shared memory is not (yet) mapped. + */ + public long getSystemNonceBlock() { + return isMapped() ? nativeGetSystemNonceBlock(mPtr) : 0; + } + + /** + * Return a pointer to the system nonce cache in the shared memory region. The method is + * idempotent. + */ + @FastNative + private static native long nativeGetSystemNonceBlock(long ptr); } diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig index 07df24843d9d..25a9fbc8476c 100644 --- a/core/java/com/android/internal/os/flags.aconfig +++ b/core/java/com/android/internal/os/flags.aconfig @@ -2,6 +2,48 @@ package: "com.android.internal.os" container: "system" flag { + namespace: "ravenwood" + name: "ravenwood_flag_rw_1" + description: "Ravenwood test RW flag 1" + bug: "311370221" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "ravenwood" + name: "ravenwood_flag_rw_2" + description: "Ravenwood test RW flag 2" + bug: "311370221" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "ravenwood" + name: "ravenwood_flag_ro_1" + description: "Ravenwood test RO flag 1" + is_fixed_read_only: true + bug: "311370221" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "ravenwood" + name: "ravenwood_flag_ro_2" + description: "Ravenwood test RO flag 2" + is_fixed_read_only: true + bug: "311370221" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_apache_http_legacy_preload" namespace: "system_performance" description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library." diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index e2237f61dfbb..0e0098ebf074 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -57,7 +57,7 @@ public class MetricsLoggerWrapper { } public static void logPostGcMemorySnapshot() { - if (!com.android.libcore.Flags.nativeMetrics()) { + if (!com.android.libcore.readonly.Flags.nativeMetrics()) { return; } int pid = Process.myPid(); diff --git a/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java b/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java new file mode 100644 index 000000000000..1acb34f73002 --- /dev/null +++ b/core/java/com/android/internal/protolog/AutoClosableProtoInputStream.java @@ -0,0 +1,56 @@ +/* + * 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.internal.protolog; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoInputStream; + +import java.io.FileInputStream; +import java.io.IOException; + +public final class AutoClosableProtoInputStream implements AutoCloseable { + @NonNull + private final ProtoInputStream mProtoInputStream; + @Nullable + private final FileInputStream mFileInputStream; + + public AutoClosableProtoInputStream(@NonNull FileInputStream fileInputStream) { + mProtoInputStream = new ProtoInputStream(fileInputStream); + mFileInputStream = fileInputStream; + } + + public AutoClosableProtoInputStream(@NonNull byte[] input) { + mProtoInputStream = new ProtoInputStream(input); + mFileInputStream = null; + } + + /** + * @return the ProtoInputStream this class is wrapping + */ + @NonNull + public ProtoInputStream get() { + return mProtoInputStream; + } + + @Override + public void close() throws IOException { + if (mFileInputStream != null) { + mFileInputStream.close(); + } + } +} diff --git a/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java b/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java new file mode 100644 index 000000000000..1598766412dd --- /dev/null +++ b/core/java/com/android/internal/protolog/NoViewerConfigProtoLogImpl.java @@ -0,0 +1,90 @@ +/* + * 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.internal.protolog; + +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.protolog.common.ILogger; +import com.android.internal.protolog.common.IProtoLog; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogLevel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Class should only be used as a temporary solution to missing viewer config file on device. + * In particular this class should only be initialized in Robolectric tests, if it's being used + * otherwise please report it. + * + * @deprecated + */ +@Deprecated +public class NoViewerConfigProtoLogImpl implements IProtoLog { + private static final String LOG_TAG = "ProtoLog"; + + @Override + public void log(LogLevel logLevel, IProtoLogGroup group, long messageHash, int paramsMask, + Object[] args) { + Log.w(LOG_TAG, "ProtoLogging is not available due to missing viewer config file..."); + logMessage(logLevel, group.getTag(), "PROTOLOG#" + messageHash + "(" + + Arrays.stream(args).map(Object::toString).collect(Collectors.joining()) + ")"); + } + + @Override + public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object... args) { + logMessage(logLevel, group.getTag(), TextUtils.formatSimple(messageString, args)); + } + + @Override + public boolean isProtoEnabled() { + return false; + } + + @Override + public int startLoggingToLogcat(String[] groups, ILogger logger) { + return 0; + } + + @Override + public int stopLoggingToLogcat(String[] groups, ILogger logger) { + return 0; + } + + @Override + public boolean isEnabled(IProtoLogGroup group, LogLevel level) { + return false; + } + + @Override + public List<IProtoLogGroup> getRegisteredGroups() { + return List.of(); + } + + private void logMessage(LogLevel logLevel, String tag, String message) { + switch (logLevel) { + case VERBOSE -> Log.v(tag, message); + case INFO -> Log.i(tag, message); + case DEBUG -> Log.d(tag, message); + case WARN -> Log.w(tag, message); + case ERROR -> Log.e(tag, message); + case WTF -> Log.wtf(tag, message); + } + } +} diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index a037cb421b0c..a1c987f79304 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -60,18 +60,16 @@ import android.util.ArraySet; import android.util.Log; import android.util.LongArray; import android.util.Slog; -import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; import com.android.internal.protolog.common.ILogger; import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogDataType; import com.android.internal.protolog.common.LogLevel; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; @@ -93,26 +91,18 @@ import java.util.stream.Stream; /** * A service for the ProtoLog logging system. */ -public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog { +public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog { private static final String LOG_TAG = "ProtoLog"; public static final String NULL_STRING = "null"; private final AtomicInteger mTracingInstances = new AtomicInteger(); @NonNull - private final ProtoLogDataSource mDataSource; - @Nullable - private final ProtoLogViewerConfigReader mViewerConfigReader; - @Deprecated - @Nullable - private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider; + protected final ProtoLogDataSource mDataSource; @NonNull - private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); + protected final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); @NonNull private final Runnable mCacheUpdater; - @Nullable // null when the flag android.tracing.client_side_proto_logging is not flipped - private final IProtoLogConfigurationService mProtoLogConfigurationService; - @NonNull private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length]; @NonNull @@ -121,68 +111,15 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto private final Map<String, Integer> mCollectStackTraceGroupCounts = new ArrayMap<>(); private final Lock mBackgroundServiceLock = new ReentrantLock(); - private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor(); - - public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) - throws ServiceManager.ServiceNotFoundException { - this(null, null, null, () -> {}, groups); - } - - public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) - throws ServiceManager.ServiceNotFoundException { - this(null, null, null, cacheUpdater, groups); - } - - public PerfettoProtoLogImpl( - @NonNull String viewerConfigFilePath, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(viewerConfigFilePath, - null, - new ProtoLogViewerConfigReader(() -> { - try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); - } catch (FileNotFoundException e) { - throw new RuntimeException( - "Failed to load viewer config file " + viewerConfigFilePath, e); - } - }), - cacheUpdater, groups); - } - - @Deprecated - @VisibleForTesting - public PerfettoProtoLogImpl( - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, - @NonNull ProtoLogConfigurationService configurationService) { - this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater, - groups, dataSourceBuilder, configurationService); - } + protected ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor(); - @VisibleForTesting - public PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, - @NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, - @NonNull ProtoLogConfigurationService configurationService) { - this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater, - groups, dataSourceBuilder, configurationService); - } + // Set to true once this is ready to accept protolog to logcat requests. + private boolean mLogcatReady = false; - private PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, + protected PerfettoProtoLogImpl( @NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader, - cacheUpdater, groups, + this(cacheUpdater, groups, ProtoLogDataSource::new, android.tracing.Flags.clientSideProtoLogging() ? IProtoLogConfigurationService.Stub.asInterface( @@ -191,19 +128,11 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto ); } - private PerfettoProtoLogImpl( - @Nullable String viewerConfigFilePath, - @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @Nullable ProtoLogViewerConfigReader viewerConfigReader, + protected PerfettoProtoLogImpl( @NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups, @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, @Nullable IProtoLogConfigurationService configurationService) { - if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) { - throw new RuntimeException("Only one of viewerConfigFilePath and " - + "viewerConfigInputStreamProvider can be set"); - } - mDataSource = dataSourceBuilder.build( this::onTracingInstanceStart, this::onTracingFlush, @@ -219,55 +148,33 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto // for some messages logged right after the construction of this class. mDataSource.register(params); - if (viewerConfigInputStreamProvider == null && viewerConfigFilePath != null) { - viewerConfigInputStreamProvider = new ViewerConfigInputStreamProvider() { - @NonNull - @Override - public ProtoInputStream getInputStream() { - try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); - } catch (FileNotFoundException e) { - throw new RuntimeException( - "Failed to load viewer config file " + viewerConfigFilePath, e); - } - } - }; - } - - this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; - this.mViewerConfigReader = viewerConfigReader; this.mCacheUpdater = cacheUpdater; registerGroupsLocally(groups); if (android.tracing.Flags.clientSideProtoLogging()) { - mProtoLogConfigurationService = configurationService; - Objects.requireNonNull(mProtoLogConfigurationService, + Objects.requireNonNull(configurationService, "A null ProtoLog Configuration Service was provided!"); try { - var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs(); - - if (viewerConfigFilePath != null) { - args.setViewerConfigFile(viewerConfigFilePath); - } + var args = createConfigurationServiceRegisterClientArgs(); final var groupArgs = Stream.of(groups) - .map(group -> new ProtoLogConfigurationServiceImpl.RegisterClientArgs + .map(group -> new RegisterClientArgs .GroupConfig(group.name(), group.isLogToLogcat())) - .toArray(ProtoLogConfigurationServiceImpl - .RegisterClientArgs.GroupConfig[]::new); + .toArray(RegisterClientArgs.GroupConfig[]::new); args.setGroups(groupArgs); - mProtoLogConfigurationService.registerClient(this, args); + configurationService.registerClient(this, args); } catch (RemoteException e) { throw new RuntimeException("Failed to register ProtoLog client"); } - } else { - mProtoLogConfigurationService = null; } } + @NonNull + protected abstract RegisterClientArgs createConfigurationServiceRegisterClientArgs(); + /** * Main log method, do not call directly. */ @@ -334,9 +241,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto * @return status code */ public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) { - if (mViewerConfigReader != null) { - mViewerConfigReader.loadViewerConfig(groups, logger); - } return setTextLogging(true, logger, groups); } @@ -347,9 +251,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto * @return status code */ public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) { - if (mViewerConfigReader != null) { - mViewerConfigReader.unloadViewerConfig(groups, logger); - } return setTextLogging(false, logger, groups); } @@ -372,21 +273,8 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto // we might want to manually specify an id for the group with a collision verifyNoCollisionsOrDuplicates(protoLogGroups); - final var groupsLoggingToLogcat = new ArrayList<String>(); for (IProtoLogGroup protoLogGroup : protoLogGroups) { mLogGroups.put(protoLogGroup.name(), protoLogGroup); - - if (protoLogGroup.isLogToLogcat()) { - groupsLoggingToLogcat.add(protoLogGroup.name()); - } - } - - if (mViewerConfigReader != null) { - // Load in background to avoid delay in boot process. - // The caveat is that any log message that is also logged to logcat will not be - // successfully decoded until this completes. - mBackgroundLoggingService.execute(() -> mViewerConfigReader - .loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0]))); } } @@ -403,6 +291,10 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto } } + protected void readyToLogToLogcat() { + mLogcatReady = true; + } + /** * Responds to a shell command. */ @@ -499,57 +391,21 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto } @Deprecated - private void dumpViewerConfig() { - if (mViewerConfigInputStreamProvider == null) { - // No viewer config available - return; - } - - Log.d(LOG_TAG, "Dumping viewer config to trace"); + abstract void dumpViewerConfig(); - Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider); - - Log.d(LOG_TAG, "Dumped viewer config to trace"); - } + @NonNull + abstract String getLogcatMessageString(@NonNull Message message); - private void logToLogcat(String tag, LogLevel level, Message message, + private void logToLogcat(@NonNull String tag, @NonNull LogLevel level, @NonNull Message message, @Nullable Object[] args) { - String messageString; - if (mViewerConfigReader == null) { - messageString = message.getMessage(); - - if (messageString == null) { - Log.e(LOG_TAG, "Failed to decode message for logcat. " - + "Message not available without ViewerConfig to decode the hash."); - } - } else { - messageString = message.getMessage(mViewerConfigReader); - - if (messageString == null) { - Log.e(LOG_TAG, "Failed to decode message for logcat. " - + "Message hash either not available in viewerConfig file or " - + "not loaded into memory from file before decoding."); - } - } - - if (messageString == null) { - StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE"); - if (args != null) { - builder.append(" args = ("); - builder.append(String.join(", ", Arrays.stream(args) - .map(it -> { - if (it == null) { - return "null"; - } else { - return it.toString(); - } - }).toList())); - builder.append(")"); - } - messageString = builder.toString(); - args = new Object[0]; + if (!mLogcatReady) { + Log.w(LOG_TAG, "Trying to log a protolog message with hash " + + message.getMessageHash() + " to logcat before the service is ready to accept " + + "such requests."); + return; } + String messageString = getLogcatMessageString(message); logToLogcat(tag, level, messageString, args); } diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java new file mode 100644 index 000000000000..febe1f3a72ac --- /dev/null +++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java @@ -0,0 +1,172 @@ +/* + * 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.internal.protolog; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; +import com.android.internal.protolog.common.ILogger; +import com.android.internal.protolog.common.IProtoLogGroup; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; + +public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { + private static final String LOG_TAG = "PerfettoProtoLogImpl"; + + @NonNull + private final ProtoLogViewerConfigReader mViewerConfigReader; + @Deprecated + @NonNull + private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider; + @NonNull + private final String mViewerConfigFilePath; + + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + this(viewerConfigFilePath, new ViewerConfigInputStreamProvider() { + @NonNull + @Override + public AutoClosableProtoInputStream getInputStream() { + try { + final var protoFileInputStream = + new FileInputStream(viewerConfigFilePath); + return new AutoClosableProtoInputStream(protoFileInputStream); + } catch (FileNotFoundException e) { + throw new RuntimeException( + "Failed to load viewer config file " + viewerConfigFilePath, e); + } + } + }, + cacheUpdater, groups); + } + + @VisibleForTesting + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups); + + this.mViewerConfigFilePath = viewerConfigFilePath; + + this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; + this.mViewerConfigReader = new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider); + + loadLogcatGroupsViewerConfig(groups); + } + + @VisibleForTesting + public ProcessedPerfettoProtoLogImpl( + @NonNull String viewerConfigFilePath, + @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, + @NonNull ProtoLogViewerConfigReader viewerConfigReader, + @NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups, + @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, + @Nullable IProtoLogConfigurationService configurationService) + throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups, dataSourceBuilder, configurationService); + + this.mViewerConfigFilePath = viewerConfigFilePath; + + this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; + this.mViewerConfigReader = viewerConfigReader; + + loadLogcatGroupsViewerConfig(groups); + } + + @NonNull + @Override + protected RegisterClientArgs createConfigurationServiceRegisterClientArgs() { + return new RegisterClientArgs() + .setViewerConfigFile(mViewerConfigFilePath); + } + + /** + * Start text logging + * @param groups Groups to start text logging for + * @param logger A logger to write status updates to + * @return status code + */ + @Override + public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) { + mViewerConfigReader.loadViewerConfig(groups, logger); + return super.startLoggingToLogcat(groups, logger); + } + + /** + * Stop text logging + * @param groups Groups to start text logging for + * @param logger A logger to write status updates to + * @return status code + */ + @Override + public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) { + mViewerConfigReader.unloadViewerConfig(groups, logger); + return super.stopLoggingToLogcat(groups, logger); + } + + @Deprecated + @Override + void dumpViewerConfig() { + Log.d(LOG_TAG, "Dumping viewer config to trace"); + Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider); + Log.d(LOG_TAG, "Dumped viewer config to trace"); + } + + @NonNull + @Override + String getLogcatMessageString(@NonNull Message message) { + String messageString; + messageString = message.getMessage(mViewerConfigReader); + + if (messageString == null) { + throw new RuntimeException("Failed to decode message for logcat. " + + "Message hash (" + message.getMessageHash() + ") either not available in " + + "viewerConfig file (" + mViewerConfigFilePath + ") or " + + "not loaded into memory from file before decoding."); + } + + return messageString; + } + + private void loadLogcatGroupsViewerConfig(@NonNull IProtoLogGroup[] protoLogGroups) { + final var groupsLoggingToLogcat = new ArrayList<String>(); + for (IProtoLogGroup protoLogGroup : protoLogGroups) { + if (protoLogGroup.isLogToLogcat()) { + groupsLoggingToLogcat.add(protoLogGroup.name()); + } + } + + // Load in background to avoid delay in boot process. + // The caveat is that any log message that is also logged to logcat will not be + // successfully decoded until this completes. + mBackgroundLoggingService.execute(() -> { + mViewerConfigReader.loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0])); + readyToLogToLogcat(); + }); + } +} diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java index 60213b1830c6..d117e93d7de7 100644 --- a/core/java/com/android/internal/protolog/ProtoLog.java +++ b/core/java/com/android/internal/protolog/ProtoLog.java @@ -70,16 +70,16 @@ public class ProtoLog { // directly to the generated tracing implementations. if (android.tracing.Flags.perfettoProtologTracing()) { synchronized (sInitLock) { + final var allGroups = new HashSet<>(Arrays.stream(groups).toList()); if (sProtoLogInstance != null) { // The ProtoLog instance has already been initialized in this process final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups(); - final var allGroups = new HashSet<>(alreadyRegisteredGroups); - allGroups.addAll(Arrays.stream(groups).toList()); - groups = allGroups.toArray(new IProtoLogGroup[0]); + allGroups.addAll(alreadyRegisteredGroups); } try { - sProtoLogInstance = new PerfettoProtoLogImpl(groups); + sProtoLogInstance = new UnprocessedPerfettoProtoLogImpl( + allGroups.toArray(new IProtoLogGroup[0])); } catch (ServiceManager.ServiceNotFoundException e) { throw new RuntimeException(e); } diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java index 8d37899f8602..e9a8770deb73 100644 --- a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java @@ -379,7 +379,7 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ @NonNull String viewerConfigFilePath) { Utils.dumpViewerConfig(dataSource, () -> { try { - return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); + return new AutoClosableProtoInputStream(new FileInputStream(viewerConfigFilePath)); } catch (FileNotFoundException e) { throw new RuntimeException( "Failed to load viewer config file " + viewerConfigFilePath, e); diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 5d67534b1b44..3378d08e7761 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -105,31 +105,10 @@ public class ProtoLogImpl { + "viewerConfigPath = " + sViewerConfigPath); final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]); - if (android.tracing.Flags.perfettoProtologTracing()) { - try { - File f = new File(sViewerConfigPath); - if (!ProtoLog.REQUIRE_PROTOLOGTOOL && !f.exists()) { - // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 - // In some tests the viewer config file might not exist in which we don't - // want to provide config path to the user - Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up " - + ProtoLogImpl.class.getSimpleName() + ". " - + "Setting up without a viewer config instead..."); - - sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups); - } else { - sServiceInstance = - new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups); - } - } catch (ServiceManager.ServiceNotFoundException e) { - throw new RuntimeException(e); - } + sServiceInstance = createProtoLogImpl(groups); } else { - var protologImpl = new LegacyProtoLogImpl( - sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater); - protologImpl.registerGroups(groups); - sServiceInstance = protologImpl; + sServiceInstance = createLegacyProtoLogImpl(groups); } sCacheUpdater.run(); @@ -137,6 +116,34 @@ public class ProtoLogImpl { return sServiceInstance; } + private static IProtoLog createProtoLogImpl(IProtoLogGroup[] groups) { + try { + File f = new File(sViewerConfigPath); + if (!f.exists()) { + // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 + // In robolectric tests the viewer config file isn't current available, so we cannot + // use the ProcessedPerfettoProtoLogImpl. + Log.e(LOG_TAG, "Failed to find viewer config file " + sViewerConfigPath + + " when setting up " + ProtoLogImpl.class.getSimpleName() + ". " + + "ProtoLog will not work here!"); + + return new NoViewerConfigProtoLogImpl(); + } else { + return new ProcessedPerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups); + } + } catch (ServiceManager.ServiceNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static LegacyProtoLogImpl createLegacyProtoLogImpl(IProtoLogGroup[] groups) { + var protologImpl = new LegacyProtoLogImpl( + sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater); + protologImpl.registerGroups(groups); + + return protologImpl; + } + @VisibleForTesting public static synchronized void setSingleInstance(@Nullable IProtoLog instance) { sServiceInstance = instance; diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index 571fe0ba37b2..524f64225084 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -106,46 +106,47 @@ public class ProtoLogViewerConfigReader { long targetGroupId = loadGroupId(group); final Map<Long, String> hashesForGroup = new TreeMap<>(); - final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream(); - - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - if (pis.getFieldNumber() == (int) MESSAGES) { - final long inMessageToken = pis.start(MESSAGES); - - long messageId = 0; - String message = null; - int groupId = 0; - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (pis.getFieldNumber()) { - case (int) MESSAGE_ID: - messageId = pis.readLong(MESSAGE_ID); - break; - case (int) MESSAGE: - message = pis.readString(MESSAGE); - break; - case (int) GROUP_ID: - groupId = pis.readInt(GROUP_ID); - break; + try (var pisWrapper = mViewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (pis.getFieldNumber() == (int) MESSAGES) { + final long inMessageToken = pis.start(MESSAGES); + + long messageId = 0; + String message = null; + int groupId = 0; + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) MESSAGE_ID: + messageId = pis.readLong(MESSAGE_ID); + break; + case (int) MESSAGE: + message = pis.readString(MESSAGE); + break; + case (int) GROUP_ID: + groupId = pis.readInt(GROUP_ID); + break; + } } - } - if (groupId == 0) { - throw new IOException("Failed to get group id"); - } + if (groupId == 0) { + throw new IOException("Failed to get group id"); + } - if (messageId == 0) { - throw new IOException("Failed to get message id"); - } + if (messageId == 0) { + throw new IOException("Failed to get message id"); + } - if (message == null) { - throw new IOException("Failed to get message string"); - } + if (message == null) { + throw new IOException("Failed to get message string"); + } - if (groupId == targetGroupId) { - hashesForGroup.put(messageId, message); - } + if (groupId == targetGroupId) { + hashesForGroup.put(messageId, message); + } - pis.end(inMessageToken); + pis.end(inMessageToken); + } } } @@ -153,30 +154,32 @@ public class ProtoLogViewerConfigReader { } private long loadGroupId(@NonNull String group) throws IOException { - final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream(); - - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - if (pis.getFieldNumber() == (int) GROUPS) { - final long inMessageToken = pis.start(GROUPS); - - long groupId = 0; - String groupName = null; - while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (pis.getFieldNumber()) { - case (int) ID: - groupId = pis.readInt(ID); - break; - case (int) NAME: - groupName = pis.readString(NAME); - break; + try (var pisWrapper = mViewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); + + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (pis.getFieldNumber() == (int) GROUPS) { + final long inMessageToken = pis.start(GROUPS); + + long groupId = 0; + String groupName = null; + while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pis.getFieldNumber()) { + case (int) ID: + groupId = pis.readInt(ID); + break; + case (int) NAME: + groupName = pis.readString(NAME); + break; + } } - } - if (Objects.equals(groupName, group)) { - return groupId; - } + if (Objects.equals(groupName, group)) { + return groupId; + } - pis.end(inMessageToken); + pis.end(inMessageToken); + } } } diff --git a/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java new file mode 100644 index 000000000000..f3fe58070fa9 --- /dev/null +++ b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.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.internal.protolog; + +import android.annotation.NonNull; +import android.os.ServiceManager; + +import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs; +import com.android.internal.protolog.common.IProtoLogGroup; + +public class UnprocessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { + public UnprocessedPerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) + throws ServiceManager.ServiceNotFoundException { + this(() -> {}, groups); + } + + public UnprocessedPerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, + @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { + super(cacheUpdater, groups); + readyToLogToLogcat(); + } + + @NonNull + @Override + protected RegisterClientArgs createConfigurationServiceRegisterClientArgs() { + return new RegisterClientArgs(); + } + + @Override + void dumpViewerConfig() { + // No-op + } + + @NonNull + @Override + String getLogcatMessageString(@NonNull Message message) { + String messageString; + messageString = message.getMessage(); + + if (messageString == null) { + throw new RuntimeException("Failed to decode message for logcat. " + + "Message not available without ViewerConfig to decode the hash."); + } + + return messageString; + } +} diff --git a/core/java/com/android/internal/protolog/Utils.java b/core/java/com/android/internal/protolog/Utils.java index 00ef80ab2bdd..629682ca2e71 100644 --- a/core/java/com/android/internal/protolog/Utils.java +++ b/core/java/com/android/internal/protolog/Utils.java @@ -48,8 +48,8 @@ public class Utils { public static void dumpViewerConfig(@NonNull ProtoLogDataSource dataSource, @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) { dataSource.trace(ctx -> { - try { - ProtoInputStream pis = viewerConfigInputStreamProvider.getInputStream(); + try (var pisWrapper = viewerConfigInputStreamProvider.getInputStream()) { + final var pis = pisWrapper.get(); final ProtoOutputStream os = ctx.newTracePacket(); diff --git a/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java index 14bc8e4782f2..60c98923eb23 100644 --- a/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java +++ b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java @@ -17,12 +17,12 @@ package com.android.internal.protolog; import android.annotation.NonNull; -import android.util.proto.ProtoInputStream; public interface ViewerConfigInputStreamProvider { /** * @return a ProtoInputStream. */ @NonNull - ProtoInputStream getInputStream(); + AutoClosableProtoInputStream getInputStream(); } + diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 81b885aa626b..b5c87868af12 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -84,4 +84,5 @@ oneway interface IPhoneStateListener { void onSimultaneousCallingStateChanged(in int[] subIds); void onCarrierRoamingNtnModeChanged(in boolean active); void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible); + void onCarrierRoamingNtnAvailableServicesChanged(in int[] availableServices); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index f836cf2b9d87..ca75abdedfcc 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -123,4 +123,5 @@ interface ITelephonyRegistry { void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason); void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active); void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible); + void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices); } diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java index d3b1f972a955..51a9cf67b06f 100644 --- a/core/java/com/android/internal/widget/NotificationProgressBar.java +++ b/core/java/com/android/internal/widget/NotificationProgressBar.java @@ -21,6 +21,9 @@ import android.annotation.Nullable; import android.app.Notification.ProgressStyle; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.drawable.LayerDrawable; @@ -52,7 +55,7 @@ import java.util.TreeSet; * represent Notification ProgressStyle progress, such as for ridesharing and navigation. */ @RemoteViews.RemoteView -public class NotificationProgressBar extends ProgressBar { +public final class NotificationProgressBar extends ProgressBar { private static final String TAG = "NotificationProgressBar"; private NotificationProgressModel mProgressModel; @@ -61,7 +64,7 @@ public class NotificationProgressBar extends ProgressBar { private List<Part> mProgressDrawableParts = null; @Nullable - private Drawable mProgressTrackerDrawable = null; + private Drawable mTracker = null; public NotificationProgressBar(Context context) { this(context, null); @@ -78,28 +81,44 @@ public class NotificationProgressBar extends ProgressBar { public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.NotificationProgressBar, defStyleAttr, defStyleRes); + saveAttributeDataForStyleable(context, R.styleable.NotificationProgressBar, attrs, a, + defStyleAttr, + defStyleRes); + + // Supports setting the tracker in xml, but ProgressStyle notifications set/override it + // via {@code setProgressTrackerIcon}. + final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker); + setTracker(tracker); } /** * Setter for the notification progress model. * * @see NotificationProgressModel#fromBundle - * @see #setProgressModelAsync */ - @RemotableViewMethod(asyncImpl = "setProgressModelAsync") + @RemotableViewMethod public void setProgressModel(@Nullable Bundle bundle) { Preconditions.checkArgument(bundle != null, "Bundle shouldn't be null"); mProgressModel = NotificationProgressModel.fromBundle(bundle); + final boolean isIndeterminate = mProgressModel.isIndeterminate(); + setIndeterminate(isIndeterminate); - if (mProgressModel.isIndeterminate()) { + if (isIndeterminate) { final int indeterminateColor = mProgressModel.getIndeterminateColor(); setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor)); } else { + final int progress = mProgressModel.getProgress(); + final int progressMax = mProgressModel.getProgressMax(); mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(), mProgressModel.getPoints(), - mProgressModel.getProgress(), mProgressModel.isStyledByProgress()); + progress, + progressMax, + mProgressModel.isStyledByProgress()); try { final NotificationProgressDrawable drawable = getNotificationProgressDrawable(); @@ -107,6 +126,9 @@ public class NotificationProgressBar extends ProgressBar { } catch (IllegalStateException ex) { Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex); } + + setMax(progressMax); + setProgress(progress); } } @@ -137,6 +159,13 @@ public class NotificationProgressBar extends ProgressBar { */ @RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync") public void setProgressTrackerIcon(@Nullable Icon icon) { + final Drawable progressTrackerDrawable; + if (icon != null) { + progressTrackerDrawable = icon.loadDrawable(getContext()); + } else { + progressTrackerDrawable = null; + } + setTracker(progressTrackerDrawable); } /** @@ -150,12 +179,245 @@ public class NotificationProgressBar extends ProgressBar { progressTrackerDrawable = null; } return () -> { - setProgressTrackerDrawable(progressTrackerDrawable); + setTracker(progressTrackerDrawable); }; } - private void setProgressTrackerDrawable(@Nullable Drawable drawable) { - mProgressTrackerDrawable = drawable; + private void setTracker(@Nullable Drawable tracker) { + if (isIndeterminate() && tracker != null) { + return; + } + + final boolean needUpdate = mTracker != null && tracker != mTracker; + if (needUpdate) { + mTracker.setCallback(null); + } + + if (tracker != null) { + tracker.setCallback(this); + if (canResolveLayoutDirection()) { + tracker.setLayoutDirection(getLayoutDirection()); + } + + // If we're updating get the new states + if (needUpdate && (tracker.getIntrinsicWidth() != mTracker.getIntrinsicWidth() + || tracker.getIntrinsicHeight() != mTracker.getIntrinsicHeight())) { + requestLayout(); + } + } + + mTracker = tracker; + invalidate(); + + if (needUpdate) { + updateTrackerAndBarPos(getWidth(), getHeight()); + if (tracker != null && tracker.isStateful()) { + // Note that if the states are different this won't work. + // For now, let's consider that an app bug. + tracker.setState(getDrawableState()); + } + } + } + + @Override + @RemotableViewMethod + public synchronized void setIndeterminate(boolean indeterminate) { + super.setIndeterminate(indeterminate); + + if (isIndeterminate()) { + setTracker(null); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == mTracker || super.verifyDrawable(who); + } + + @Override + public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + + if (mTracker != null) { + mTracker.jumpToCurrentState(); + } + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + + final Drawable tracker = mTracker; + if (tracker != null && tracker.isStateful() + && tracker.setState(getDrawableState())) { + invalidateDrawable(tracker); + } + } + + @Override + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); + + if (mTracker != null) { + mTracker.setHotspot(x, y); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + updateTrackerAndBarPos(w, h); + } + + private void updateTrackerAndBarPos(int w, int h) { + final int paddedHeight = h - mPaddingTop - mPaddingBottom; + final Drawable bar = getCurrentDrawable(); + final Drawable tracker = mTracker; + + // The max height does not incorporate padding, whereas the height + // parameter does. + final int barHeight = Math.min(getMaxHeight(), paddedHeight); + final int trackerHeight = tracker == null ? 0 : tracker.getIntrinsicHeight(); + + // Apply offset to whichever item is taller. + final int barOffsetY; + final int trackerOffsetY; + if (trackerHeight > barHeight) { + final int offsetHeight = (paddedHeight - trackerHeight) / 2; + barOffsetY = offsetHeight + (trackerHeight - barHeight) / 2; + trackerOffsetY = offsetHeight; + } else { + final int offsetHeight = (paddedHeight - barHeight) / 2; + barOffsetY = offsetHeight; + trackerOffsetY = offsetHeight + (barHeight - trackerHeight) / 2; + } + + if (bar != null) { + final int barWidth = w - mPaddingRight - mPaddingLeft; + bar.setBounds(0, barOffsetY, barWidth, barOffsetY + barHeight); + } + + if (tracker != null) { + setTrackerPos(w, tracker, getScale(), trackerOffsetY); + } + } + + private float getScale() { + int min = getMin(); + int max = getMax(); + int range = max - min; + return range > 0 ? (getProgress() - min) / (float) range : 0; + } + + /** + * Updates the tracker drawable bounds. + * + * @param w Width of the view, including padding + * @param tracker Drawable used for the tracker + * @param scale Current progress between 0 and 1 + * @param offsetY Vertical offset for centering. If set to + * {@link Integer#MIN_VALUE}, the current offset will be used. + */ + private void setTrackerPos(int w, Drawable tracker, float scale, int offsetY) { + int available = w - mPaddingLeft - mPaddingRight; + final int trackerWidth = tracker.getIntrinsicWidth(); + final int trackerHeight = tracker.getIntrinsicHeight(); + available -= trackerWidth; + + final int trackerPos = (int) (scale * available + 0.5f); + + final int top, bottom; + if (offsetY == Integer.MIN_VALUE) { + final Rect oldBounds = tracker.getBounds(); + top = oldBounds.top; + bottom = oldBounds.bottom; + } else { + top = offsetY; + bottom = offsetY + trackerHeight; + } + + final int left = (isLayoutRtl() && getMirrorForRtl()) ? available - trackerPos : trackerPos; + final int right = left + trackerWidth; + + final Drawable background = getBackground(); + if (background != null) { + final int bkgOffsetX = mPaddingLeft; + final int bkgOffsetY = mPaddingTop; + background.setHotspotBounds(left + bkgOffsetX, top + bkgOffsetY, + right + bkgOffsetX, bottom + bkgOffsetY); + } + + // Canvas will be translated, so 0,0 is where we start drawing + tracker.setBounds(left, top, right, bottom); + } + + @Override + public void onResolveDrawables(int layoutDirection) { + super.onResolveDrawables(layoutDirection); + + if (mTracker != null) { + mTracker.setLayoutDirection(layoutDirection); + } + } + + @Override + protected synchronized void onDraw(Canvas canvas) { + super.onDraw(canvas); + drawTracker(canvas); + } + + /** + * Draw the tracker. + */ + private void drawTracker(Canvas canvas) { + if (mTracker != null) { + final int saveCount = canvas.save(); + // Translate the padding. For the x, we need to allow the tracker to + // draw in its extra space + canvas.translate(mPaddingLeft, mPaddingTop); + mTracker.draw(canvas); + canvas.restoreToCount(saveCount); + } + } + + @Override + protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Drawable d = getCurrentDrawable(); + + int trackerHeight = mTracker == null ? 0 : mTracker.getIntrinsicHeight(); + int dw = 0; + int dh = 0; + if (d != null) { + dw = Math.max(getMinWidth(), Math.min(getMaxWidth(), d.getIntrinsicWidth())); + dh = Math.max(getMinHeight(), Math.min(getMaxHeight(), d.getIntrinsicHeight())); + dh = Math.max(trackerHeight, dh); + } + dw += mPaddingLeft + mPaddingRight; + dh += mPaddingTop + mPaddingBottom; + + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), + resolveSizeAndState(dh, heightMeasureSpec, 0)); + } + + @Override + public CharSequence getAccessibilityClassName() { + return NotificationProgressBar.class.getName(); + } + + @Override + public void onRtlPropertiesChanged(int layoutDirection) { + super.onRtlPropertiesChanged(layoutDirection); + + final Drawable tracker = mTracker; + if (tracker != null) { + setTrackerPos(getWidth(), tracker, getScale(), Integer.MIN_VALUE); + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. + invalidate(); + } } /** @@ -167,12 +429,18 @@ public class NotificationProgressBar extends ProgressBar { List<ProgressStyle.Segment> segments, List<ProgressStyle.Point> points, int progress, + int progressMax, boolean isStyledByProgress ) { if (segments.isEmpty()) { throw new IllegalArgumentException("List of segments shouldn't be empty"); } + final int totalLength = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum(); + if (progressMax != totalLength) { + throw new IllegalArgumentException("Invalid progressMax : " + progressMax); + } + for (ProgressStyle.Segment segment : segments) { final int length = segment.getLength(); if (length <= 0) { @@ -180,8 +448,6 @@ public class NotificationProgressBar extends ProgressBar { } } - final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum(); - if (progress < 0 || progress > progressMax) { throw new IllegalArgumentException("Invalid progress : " + progress); } @@ -208,7 +474,6 @@ public class NotificationProgressBar extends ProgressBar { isStyledByProgress); } - // Any segment with a point on it gets split by the point. // If isStyledByProgress is true, also split the segment with the progress value in its range. private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress( diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java index e51ea99ac4f5..e8cb37e8f19b 100644 --- a/core/java/com/android/internal/widget/NotificationProgressModel.java +++ b/core/java/com/android/internal/widget/NotificationProgressModel.java @@ -96,6 +96,10 @@ public final class NotificationProgressModel { return mProgress; } + public int getProgressMax() { + return mSegments.stream().mapToInt(Notification.ProgressStyle.Segment::getLength).sum(); + } + public boolean isStyledByProgress() { return mIsStyledByProgress; } diff --git a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS index ed9425cc26c9..999ea0e62a0a 100644 --- a/core/java/com/android/internal/widget/floatingtoolbar/OWNERS +++ b/core/java/com/android/internal/widget/floatingtoolbar/OWNERS @@ -1 +1 @@ -include /core/java/android/view/selectiontoolbar/OWNERS +include /core/java/android/permission/OWNERS diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 816ace2310a5..eb07f7c125d0 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -249,6 +249,7 @@ cc_library_shared_for_libandroid_runtime { "android_backup_BackupDataOutput.cpp", "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", + "android_app_PropertyInvalidatedCache.cpp", "android_app_backup_FullBackup.cpp", "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 76f66cd4ebc9..821861efd59b 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -177,6 +177,7 @@ extern int register_android_app_backup_FullBackup(JNIEnv *env); extern int register_android_app_Activity(JNIEnv *env); extern int register_android_app_ActivityThread(JNIEnv *env); extern int register_android_app_NativeActivity(JNIEnv *env); +extern int register_android_app_PropertyInvalidatedCache(JNIEnv* env); extern int register_android_media_RemoteDisplay(JNIEnv *env); extern int register_android_util_jar_StrictJarFile(JNIEnv* env); extern int register_android_view_InputChannel(JNIEnv* env); @@ -1659,6 +1660,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_app_Activity), REG_JNI(register_android_app_ActivityThread), REG_JNI(register_android_app_NativeActivity), + REG_JNI(register_android_app_PropertyInvalidatedCache), REG_JNI(register_android_util_jar_StrictJarFile), REG_JNI(register_android_view_InputChannel), REG_JNI(register_android_view_InputEventReceiver), diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp new file mode 100644 index 000000000000..ead66660a0a4 --- /dev/null +++ b/core/jni/android_app_PropertyInvalidatedCache.cpp @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#define LOG_TAG "CacheNonce" + +#include <string.h> +#include <memory.h> + +#include <atomic> + +#include <nativehelper/JNIHelp.h> +#include <nativehelper/scoped_primitive_array.h> +#include <android-base/logging.h> + +#include "core_jni_helpers.h" +#include "android_app_PropertyInvalidatedCache.h" + +namespace { + +using namespace android::app::PropertyInvalidatedCache; + +// Convert a jlong to a nonce block. This is a convenience function that should be inlined by +// the compiler. +inline SystemCacheNonce* sysCache(jlong ptr) { + return reinterpret_cast<SystemCacheNonce*>(ptr); +} + +// Return the number of nonces in the nonce block. +jint getMaxNonce(JNIEnv*, jclass, jlong ptr) { + return sysCache(ptr)->getMaxNonce(); +} + +// Return the number of string bytes in the nonce block. +jint getMaxByte(JNIEnv*, jclass, jlong ptr) { + return sysCache(ptr)->getMaxByte(); +} + +// Set the byte block. The first int is the hash to set and the second is the array to copy. +// This should be synchronized in the Java layer. +void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) { + ScopedByteArrayRO value(env, val); + if (value.get() == nullptr) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block"); + return; + } + sysCache(ptr)->setByteBlock(hash, value.get(), value.size()); +} + +// Fetch the byte block. If the incoming hash is the same as the local hash, the Java layer is +// presumed to have an up-to-date copy of the byte block; do not copy byte array. The local +// hash is returned. +jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) { + if (sysCache(ptr)->getHash() == hash) { + return hash; + } + ScopedByteArrayRW value(env, val); + return sysCache(ptr)->getByteBlock(value.get(), value.size()); +} + +// Fetch the byte block hash. +// +// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters. +jint getByteBlockHash(jlong ptr) { + return sysCache(ptr)->getHash(); +} + +// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is +// out of range, rather than throwing an exception. This is a CriticalNative method and +// therefore does not get the JNIEnv or jclass parameters. +// +// This method is @CriticalNative and does not take a JNIEnv* or jclass argument. +jlong getNonce(jlong ptr, jint index) { + return sysCache(ptr)->getNonce(index); +} + +// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if +// the index is out of range and true otherwise. Callers may test the returned boolean and +// generate an exception. +// +// This method is @CriticalNative and does not take a JNIEnv* or jclass argument. +jboolean setNonce(jlong ptr, jint index, jlong value) { + return sysCache(ptr)->setNonce(index, value); +} + +static const JNINativeMethod gMethods[] = { + {"nativeGetMaxNonce", "(J)I", (void*) getMaxNonce }, + {"nativeGetMaxByte", "(J)I", (void*) getMaxByte }, + {"nativeSetByteBlock", "(JI[B)V", (void*) setByteBlock }, + {"nativeGetByteBlock", "(JI[B)I", (void*) getByteBlock }, + {"nativeGetByteBlockHash", "(J)I", (void*) getByteBlockHash }, + {"nativeGetNonce", "(JI)J", (void*) getNonce }, + {"nativeSetNonce", "(JIJ)Z", (void*) setNonce }, +}; + +static const char* kClassName = "android/app/PropertyInvalidatedCache"; + +} // anonymous namespace + +namespace android { + +int register_android_app_PropertyInvalidatedCache(JNIEnv* env) { + RegisterMethodsOrDie(env, kClassName, gMethods, NELEM(gMethods)); + return JNI_OK; +} + +} // namespace android diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h new file mode 100644 index 000000000000..eefa8fa88624 --- /dev/null +++ b/core/jni/android_app_PropertyInvalidatedCache.h @@ -0,0 +1,146 @@ +/* + * 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. + */ + +#include <string.h> +#include <memory.h> + +#include <atomic> + +namespace android { +namespace app { +namespace PropertyInvalidatedCache { + +/** + * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The + * byte array has an associated hash. This class provides methods to read and write the fields + * of the block but it does not interpret the fields. + * + * On initialization, all fields are set to zero. + * + * In general, methods do not report errors. This allows the methods to be used in + * CriticalNative JNI APIs. + * + * The template is parameterized by the number of nonces it supports and the number of bytes in + * the string block. + */ +template<int maxNonce, size_t maxByte> class CacheNonce { + + // The value of an unset field. + static const int UNSET = 0; + + // A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as + // signed char. + typedef signed char block_t; + + // The array of nonces + volatile std::atomic<int64_t> mNonce[maxNonce]; + + // The byte array. This is not atomic but it is guarded by the mByteHash. + volatile block_t mByteBlock[maxByte]; + + // The hash that validates the byte block + volatile std::atomic<int32_t> mByteHash; + + // Pad the class to a multiple of 8 bytes. + int32_t _pad; + + public: + + // The expected size of this instance. This is a compile-time constant and can be used in a + // static assertion. + static const int expectedSize = + maxNonce * sizeof(std::atomic<int64_t>) + + sizeof(std::atomic<int32_t>) + + maxByte * sizeof(block_t) + + sizeof(int32_t); + + // These provide run-time access to the sizing parameters. + int getMaxNonce() const { + return maxNonce; + } + + size_t getMaxByte() const { + return maxByte; + } + + // Construct and initialize the memory. + CacheNonce() { + for (int i = 0; i < maxNonce; i++) { + mNonce[i] = UNSET; + } + mByteHash = UNSET; + memset((void*) mByteBlock, UNSET, sizeof(mByteBlock)); + } + + // Fetch a nonce, returning UNSET if the index is out of range. This method specifically + // does not throw or generate an error if the index is out of range; this allows the method + // to be called in a CriticalNative JNI API. + int64_t getNonce(int index) const { + if (index < 0 || index >= maxNonce) { + return UNSET; + } else { + return mNonce[index]; + } + } + + // Set a nonce and return true. Return false if the index is out of range. This method + // specifically does not throw or generate an error if the index is out of range; this + // allows the method to be called in a CriticalNative JNI API. + bool setNonce(int index, int64_t value) { + if (index < 0 || index >= maxNonce) { + return false; + } else { + mNonce[index] = value; + return true; + } + } + + // Fetch just the byte-block hash + int32_t getHash() const { + return mByteHash; + } + + // Copy the byte block to the target and return the current hash. + int32_t getByteBlock(block_t* block, size_t len) const { + memcpy(block, (void*) mByteBlock, std::min(maxByte, len)); + return mByteHash; + } + + // Set the byte block and the hash. + void setByteBlock(int hash, const block_t* block, size_t len) { + memcpy((void*) mByteBlock, block, len = std::min(maxByte, len)); + mByteHash = hash; + } +}; + +/** + * Sizing parameters for the system_server PropertyInvalidatedCache support. A client can + * retrieve the values through the accessors in CacheNonce instances. + */ +static const int MAX_NONCE = 64; +static const int BYTE_BLOCK_SIZE = 8192; + +// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes. +typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce; + +// The goal of this assertion is to ensure that the data structure is the same size across 32-bit +// and 64-bit systems. +static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize, + "Unexpected SystemCacheNonce size"); + +} // namespace PropertyInvalidatedCache +} // namespace app +} // namespace android diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 10e49efee92e..50252c11ffb1 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -27,6 +27,7 @@ #include <camera/StringUtils.h> #include <com_android_internal_camera_flags.h> #include <cutils/properties.h> +#include <gui/Flags.h> #include <gui/GLConsumer.h> #include <gui/Surface.h> #include <nativehelper/JNIHelp.h> @@ -715,16 +716,20 @@ static void android_hardware_Camera_setPreviewSurface(JNIEnv *env, jobject thiz, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; - sp<IGraphicBufferProducer> gbp; sp<Surface> surface; if (jSurface) { surface = android_view_Surface_getSurface(env, jSurface); - if (surface != NULL) { - gbp = surface->getIGraphicBufferProducer(); - } } +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + if (camera->setPreviewTarget(surface) != NO_ERROR) { +#else + sp<IGraphicBufferProducer> gbp; + if (surface != NULL) { + gbp = surface->getIGraphicBufferProducer(); + } if (camera->setPreviewTarget(gbp) != NO_ERROR) { +#endif jniThrowException(env, "java/io/IOException", "setPreviewTexture failed"); } } @@ -736,6 +741,9 @@ static void android_hardware_Camera_setPreviewTexture(JNIEnv *env, sp<Camera> camera = get_native_camera(env, thiz, NULL); if (camera == 0) return; +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + sp<Surface> surface; +#endif sp<IGraphicBufferProducer> producer = NULL; if (jSurfaceTexture != NULL) { producer = SurfaceTexture_getProducer(env, jSurfaceTexture); @@ -744,10 +752,16 @@ static void android_hardware_Camera_setPreviewTexture(JNIEnv *env, "SurfaceTexture already released in setPreviewTexture"); return; } - +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + surface = new Surface(producer); +#endif } +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + if (camera->setPreviewTarget(surface) != NO_ERROR) { +#else if (camera->setPreviewTarget(producer) != NO_ERROR) { +#endif jniThrowException(env, "java/io/IOException", "setPreviewTexture failed"); } @@ -761,18 +775,32 @@ static void android_hardware_Camera_setPreviewCallbackSurface(JNIEnv *env, sp<Camera> camera = get_native_camera(env, thiz, &context); if (camera == 0) return; +#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES sp<IGraphicBufferProducer> gbp; +#endif sp<Surface> surface; if (jSurface) { surface = android_view_Surface_getSurface(env, jSurface); + if (surface == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "android_view_Surface_getSurface failed"); + return; + } + +#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES if (surface != NULL) { gbp = surface->getIGraphicBufferProducer(); } +#endif } // Clear out normal preview callbacks context->setCallbackMode(env, false, false); // Then set up callback surface +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + if (camera->setPreviewCallbackTarget(surface) != NO_ERROR) { +#else if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) { +#endif jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed"); } } diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp index 63de1950f2a5..bb4084e8f39e 100644 --- a/core/jni/android_hardware_OverlayProperties.cpp +++ b/core/jni/android_hardware_OverlayProperties.cpp @@ -213,6 +213,6 @@ int register_android_hardware_OverlayProperties(JNIEnv* env) { clazz = FindClassOrDie(env, "android/hardware/LutProperties"); gLutPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gLutPropertiesClassInfo.ctor = - GetMethodIDOrDie(env, gLutPropertiesClassInfo.clazz, "<init>", "(IJ[I)V"); + GetMethodIDOrDie(env, gLutPropertiesClassInfo.clazz, "<init>", "(II[I)V"); return err; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 755704a5ad91..f162b7410b10 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -35,6 +35,7 @@ #include <android_runtime/android_view_SurfaceSession.h> #include <cutils/ashmem.h> #include <gui/ISurfaceComposer.h> +#include <gui/JankInfo.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <jni.h> @@ -2161,9 +2162,30 @@ public: jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(), gJankDataClassInfo.clazz, nullptr); for (size_t i = 0; i < jankData.size(); i++) { + // The exposed constants in SurfaceControl are simplified, so we need to translate the + // jank type we get from SF to what is exposed in Java. + int sfJankType = jankData[i].jankType; + int javaJankType = 0x0; // SurfaceControl.JankData.JANK_NONE + if (sfJankType & + (JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed | + JankType::SurfaceFlingerGpuDeadlineMissed | JankType::PredictionError | + JankType::SurfaceFlingerScheduling)) { + javaJankType |= 0x1; // SurfaceControl.JankData.JANK_COMPOSER + } + if (sfJankType & JankType::AppDeadlineMissed) { + javaJankType |= 0x2; // SurfaceControl.JankData.JANK_APPLICATION + } + if (sfJankType & + ~(JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed | + JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed | + JankType::PredictionError | JankType::SurfaceFlingerScheduling | + JankType::BufferStuffing | JankType::SurfaceFlingerStuffing)) { + javaJankType |= 0x4; // SurfaceControl.JankData.JANK_OTHER + } + jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor, - jankData[i].frameVsyncId, jankData[i].jankType, + jankData[i].frameVsyncId, javaJankType, jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs, jankData[i].actualAppFrameTimeNs); env->SetObjectArrayElement(jJankDataArray, i, jJankData); diff --git a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp index 453e53974e0d..cc1687cd9ffb 100644 --- a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp +++ b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp @@ -29,8 +29,12 @@ #include "core_jni_helpers.h" +#include "android_app_PropertyInvalidatedCache.h" + namespace { +using namespace android::app::PropertyInvalidatedCache; + // Atomics should be safe to use across processes if they are lock free. static_assert(std::atomic<int64_t>::is_always_lock_free == true, "atomic<int64_t> is not always lock free"); @@ -64,12 +68,15 @@ public: void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) { latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset; } + + // The nonce storage for pic. The sizing is suitable for the system server module. + SystemCacheNonce systemPic; }; // Update the expected value when modifying the members of SharedMemory. // The goal of this assertion is to ensure that the data structure is the same size across 32-bit // and 64-bit systems. -static_assert(sizeof(SharedMemory) == 8, "Unexpected SharedMemory size"); +static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size"); static jint nativeCreate(JNIEnv* env, jclass) { // Create anonymous shared memory region @@ -133,6 +140,12 @@ static jlong nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMilli return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(); } +// This is a FastNative method. It takes the usual JNIEnv* and jclass* arguments. +static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) { + SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr); + return reinterpret_cast<jlong>(&sharedMemory->systemPic); +} + static const JNINativeMethod gMethods[] = { {"nativeCreate", "()I", (void*)nativeCreate}, {"nativeMap", "(IZ)J", (void*)nativeMap}, @@ -143,16 +156,17 @@ static const JNINativeMethod gMethods[] = { (void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis}, {"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J", (void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis}, + {"nativeGetSystemNonceBlock", "(J)J", (void*) nativeGetSystemNonceBlock}, }; -} // anonymous namespace - -namespace android { - static const char kApplicationSharedMemoryClassName[] = "com/android/internal/os/ApplicationSharedMemory"; static jclass gApplicationSharedMemoryClass; +} // anonymous namespace + +namespace android { + int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) { gApplicationSharedMemoryClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName)); diff --git a/core/res/Android.bp b/core/res/Android.bp index aa324fcaca58..f6ca8218926c 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -158,6 +158,7 @@ android_app { flags_packages: [ "android.app.appfunctions.flags-aconfig", "android.app.contextualsearch.flags-aconfig", + "android.appwidget.flags-aconfig", "android.content.pm.flags-aconfig", "android.provider.flags-aconfig", "camera_platform_flags", @@ -169,6 +170,7 @@ android_app { "android.media.tv.flags-aconfig", "android.security.flags-aconfig", "com.android.hardware.input.input-aconfig", + "aconfig_trade_in_mode_flags", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6ab64768d9f0..4c38246b35b0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1494,8 +1494,8 @@ android:description="@string/permdesc_readBasicPhoneState" android:protectionLevel="normal" /> - <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities - granted by {@link #READ_PHONE_STATE} but is exposed to instant applications. + <!-- Allows read access to the device's phone number(s), + which is exposed to instant applications. <p>Protection level: dangerous--> <permission android:name="android.permission.READ_PHONE_NUMBERS" android:permissionGroup="android.permission-group.UNDEFINED" @@ -8464,6 +8464,14 @@ <permission android:name="android.permission.SETUP_FSVERITY" android:protectionLevel="signature|privileged"/> + <!-- Allows app to enter trade-in-mode. + <p>Protection level: signature|privileged + @hide + --> + <permission android:name="android.permission.ENTER_TRADE_IN_MODE" + android:protectionLevel="signature|privileged" + android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" /> + <!-- @TestApi Signature permission reserved for testing. This should never be used to @@ -8475,6 +8483,27 @@ <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" android:protectionLevel="signature"/> + <!-- + @SystemApi + @FlaggedApi("android.media.tv.flags.media_quality_fw") + Allows an application to access its picture profile from the media quality database. + <p> Protection level: signature|privileged|vendor privileged + @hide + --> + <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE" + android:protectionLevel="signature|privileged|vendorPrivileged" + android:featureFlag="android.media.tv.flags.media_quality_fw"/> + + <!-- + @SystemApi + @FlaggedApi("android.media.tv.flags.media_quality_fw") + Allows an application to access its sound profile from the media quality database. + <p> Protection level: signature|privileged|vendor privileged + @hide + --> + <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" + android:protectionLevel="signature|privileged|vendorPrivileged" + android:featureFlag="android.media.tv.flags.media_quality_fw"/> <!-- @SystemApi @FlaggedApi("android.content.pm.verification_service") Allows app to be the verification agent to verify packages. diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index ad8f056a8609..ad0809f7e501 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -678,8 +678,8 @@ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"مهلت تنظیم اثر انگشت بهپایان رسید. دوباره امتحان کنید."</string> <string name="fingerprint_error_canceled" msgid="5541771463159727513">"عملکرد اثر انگشت لغو شد"</string> <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"کاربر عملیات اثر انگشت را لغو کرد"</string> - <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاشها از حد مجاز بیشتر شده است. بهجای آن از قفل صفحه استفاده کنید."</string> - <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاشهای بیشازحد. حالا از قفل صفحه استفاده کنید."</string> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاشهای بسیار زیاد ناموفق. بهجای آن، از قفل صفحه استفاده کنید."</string> + <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاشهای بسیار زیاد ناموفق. حالا از قفل صفحه استفاده کنید."</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"اثر انگشت پردازش نشد. دوباره امتحان کنید."</string> <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"اثر انگشتی ثبت نشده است"</string> <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"این دستگاه حسگر اثر انگشت ندارد"</string> @@ -740,9 +740,9 @@ <string name="face_error_no_space" msgid="5649264057026021723">"داده چهره جدید ذخیره نشد. اول داده چهره قدیمی را حذف کنید."</string> <string name="face_error_canceled" msgid="2164434737103802131">"عملیات شناسایی چهره لغو شد."</string> <string name="face_error_user_canceled" msgid="5766472033202928373">"کاربر «قفلگشایی با چهره» را لغو کرد"</string> - <string name="face_error_lockout" msgid="7864408714994529437">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string> - <string name="face_error_lockout_permanent" msgid="8533257333130473422">"تعداد تلاشها از حد مجاز بیشتر شده است. قفلگشایی با چهره دردسترس نیست."</string> - <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"تلاشها بیش از حدمجاز شده است. درعوض قفل صفحه را وارد کنید."</string> + <string name="face_error_lockout" msgid="7864408714994529437">"تلاشهای بسیار زیاد ناموفق. بعداً دوباره امتحان کنید."</string> + <string name="face_error_lockout_permanent" msgid="8533257333130473422">"تلاشهای بسیار زیاد ناموفق. قفلگشایی با چهره دردسترس نیست."</string> + <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"تلاشهای بسیار زیاد ناموفق. درعوض، قفل صفحه را وارد کنید."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"چهره تأیید نشد. دوباره امتحان کنید."</string> <string name="face_error_not_enrolled" msgid="1134739108536328412">"«قفلگشایی با چهره» را راهاندازی نکردهاید"</string> <string name="face_error_hw_not_present" msgid="7940978724978763011">"از «قفلگشایی با چهره» در این دستگاه پشتیبانی نمیشود"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index df7006ace657..41f2de05d303 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -234,7 +234,7 @@ <string name="turn_on_radio" msgid="2961717788170634233">"បើកបណ្ដាញឥតខ្សែ"</string> <string name="turn_off_radio" msgid="7222573978109933360">"បិទបណ្ដាញឥតខ្សែ"</string> <string name="screen_lock" msgid="2072642720826409809">"ចាក់សោអេក្រង់"</string> - <string name="power_off" msgid="4111692782492232778">"បិទ"</string> + <string name="power_off" msgid="4111692782492232778">"បិទថាមពល"</string> <string name="silent_mode_silent" msgid="5079789070221150912">"បិទកម្មវិធីរោទ៍"</string> <string name="silent_mode_vibrate" msgid="8821830448369552678">"កម្មវិធីរោទ៍ញ័រ"</string> <string name="silent_mode_ring" msgid="6039011004781526678">"បើកកម្មវិធីរោទ៍"</string> @@ -258,11 +258,11 @@ <string name="global_actions" product="tv" msgid="3871763739487450369">"ជម្រើស Android TV"</string> <string name="global_actions" product="default" msgid="6410072189971495460">"ជម្រើសទូរស័ព្ទ"</string> <string name="global_action_lock" msgid="6949357274257655383">"ចាក់សោអេក្រង់"</string> - <string name="global_action_power_off" msgid="4404936470711393203">"បិទ"</string> + <string name="global_action_power_off" msgid="4404936470711393203">"បិទថាមពល"</string> <string name="global_action_power_options" msgid="1185286119330160073">"ថាមពល"</string> <string name="global_action_restart" msgid="4678451019561687074">"ចាប់ផ្ដើមឡើងវិញ"</string> <string name="global_action_emergency" msgid="1387617624177105088">"អាសន្ន"</string> - <string name="global_action_bug_report" msgid="5127867163044170003">"របាយការណ៍កំហុស"</string> + <string name="global_action_bug_report" msgid="5127867163044170003">"របាយការណ៍អំពីបញ្ហា"</string> <string name="global_action_logout" msgid="6093581310002476511">"បញ្ចប់សម័យ"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"រូបថតអេក្រង់"</string> <string name="bugreport_title" msgid="8549990811777373050">"របាយការណ៍អំពីបញ្ហា"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 02f9f3c5f0db..990cbb47540a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -5731,6 +5731,12 @@ </attr> </declare-styleable> + <!-- @hide internal use only --> + <declare-styleable name="NotificationProgressBar"> + <!-- Draws the tracker on a NotificationProgressBar. --> + <attr name="tracker" format="reference" /> + </declare-styleable> + <declare-styleable name="StackView"> <!-- Color of the res-out outline. --> <attr name="resOutColor" format="color" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 42ac90dd8066..b55e5f08f7c7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7054,6 +7054,9 @@ RSS is being collected instead. --> <item name="config_am_pssToRssThresholdModifier" format="float" type="dimen">1.5</item> + <!-- The size of the UI tier if cached app oom_adj_score tiers are enabled. --> + <integer name="config_am_tieredCachedAdjUiTierSize">10</integer> + <!-- Whether unlocking and waking a device are sequenced --> <bool name="config_orderUnlockAndWake">false</bool> @@ -7178,4 +7181,7 @@ <!-- The name of the service for forensic backup transport. --> <string name="config_forensicBackupTransport" translatable="false"></string> + + <!-- Whether to enable fp unlock when screen turns off on udfps devices --> + <bool name="config_screen_off_udfps_enabled">false</bool> </resources> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 9854030ed0d1..b5892f6e1a77 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -460,4 +460,16 @@ <integer name="config_satellite_location_query_throttle_interval_minutes">10</integer> <java-symbol type="integer" name="config_satellite_location_query_throttle_interval_minutes" /> + <!-- Boolean indicating whether to enable MT SMS polling for NB IOT NTN. --> + <bool name="config_enabled_mt_sms_polling">true</bool> + <java-symbol type="bool" name="config_enabled_mt_sms_polling" /> + + <!-- Text to be used for MT SMS polling in NB IOT NTN. --> + <string name="config_mt_sms_polling_text" translatable="false">DU\\\#MMYSM€S2BIG\\\#NORED\\\!</string> + <java-symbol type="string" name="config_mt_sms_polling_text" /> + + <!-- The time duration in millis after which Telephony can send another MT SMS polling for NB IOT NTN --> + <integer name="config_mt_sms_polling_throttle_millis">300000</integer> + <java-symbol type="integer" name="config_mt_sms_polling_throttle_millis" /> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 7184d9a8c890..522dcfaf4729 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -17,7 +17,7 @@ ** limitations under the License. */ --> -<resources> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> <!-- The width that is used when creating thumbnails of applications. --> <dimen name="thumbnail_width">192dp</dimen> <!-- The height that is used when creating thumbnails of applications. --> @@ -1037,9 +1037,12 @@ <dimen name="controls_thumbnail_image_max_width">280dp</dimen> <!-- System-provided radius for the background view of app widgets. The resolved value of this resource may change at runtime. --> - <dimen name="system_app_widget_background_radius">28dp</dimen> - <!-- System-provided radius for inner views on app widgets. The resolved value of this resource may change at runtime. --> - <dimen name="system_app_widget_inner_radius">20dp</dimen> + <dimen name="system_app_widget_background_radius" android:featureFlag="!android.appwidget.flags.use_smaller_app_widget_radius">28dp</dimen> + <dimen name="system_app_widget_background_radius" android:featureFlag="android.appwidget.flags.use_smaller_app_widget_radius">24dp</dimen> + <!-- System-provided radius for inner views on app widgets that are positioned 8dp within the widget background view. The resolved value of this resource may change at runtime. --> + <dimen name="system_app_widget_inner_radius" android:featureFlag="!android.appwidget.flags.use_smaller_app_widget_radius">20dp</dimen> + <!-- System-provided radius for inner views on app widgets that are positioned 8dp within the widget background view. The resolved value of this resource may change at runtime. --> + <dimen name="system_app_widget_inner_radius" android:featureFlag="android.appwidget.flags.use_smaller_app_widget_radius">16dp</dimen> <!-- System-provided padding for inner views on app widgets. The resolved value of this resource may change at runtime. @removed --> <dimen name="__removed_system_app_widget_internal_padding">16dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index dfee85a3788d..ecc3afe7dbe2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5499,6 +5499,9 @@ <java-symbol type="bool" name="config_am_disablePssProfiling" /> <java-symbol type="dimen" name="config_am_pssToRssThresholdModifier" /> + <!-- For OomAdjuster cached app tiers configurability --> + <java-symbol type="integer" name="config_am_tieredCachedAdjUiTierSize" /> + <java-symbol type="raw" name="default_ringtone_vibration_effect" /> <!-- For activity embedding divider --> @@ -5639,4 +5642,7 @@ <!-- Forensic backup transport --> <java-symbol type="string" name="config_forensicBackupTransport" /> + + <!-- Fingerprint screen off unlock config --> + <java-symbol type="bool" name="config_screen_off_udfps_enabled" /> </resources> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 581dee571a69..bb5380e1312d 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -34,7 +34,7 @@ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ --> <!-- Arab Emirates --> - <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" /> + <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568" /> <!-- Albania: 5 digits, known short codes listed --> <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" /> @@ -70,7 +70,7 @@ <shortcode country="bh" pattern="\\d{1,5}" free="81181|85999" /> <!-- Brazil: 1-5 digits (standard system default, not country specific) --> - <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652" /> + <shortcode country="br" pattern="\\d{1,5}" free="6000[012]\\d|876|5500|9963|4141|8000|2652|26808" /> <!-- Botswana: 1-5 digits (standard system default, not country specific) --> <shortcode country="bw" pattern="\\d{1,5}" free="16641" /> @@ -79,7 +79,7 @@ <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" /> <!-- Canada: 5-6 digits --> - <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677" /> + <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677|24470" /> <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf --> <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" /> @@ -123,8 +123,8 @@ http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht --> <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" /> - <!-- Egypt: 4 digits, known codes listed --> - <shortcode country="eg" pattern="\\d{4}" free="1499" /> + <!-- Egypt: 4-5 digits, known codes listed --> + <shortcode country="eg" pattern="\\d{4,5}" free="1499|10020" /> <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU. http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain --> @@ -147,7 +147,7 @@ <shortcode country="ge" pattern="\\d{1,5}" premium="801[234]|888[239]" free="95201|95202|95203" /> <!-- Ghana: 4 digits, known premium codes listed --> - <shortcode country="gh" pattern="\\d{4}" free="5041|3777|2333" /> + <shortcode country="gh" pattern="\\d{4}" free="5041|3777|2333|6061" /> <!-- Greece: 5 digits (54xxx, 19yxx, x=0-9, y=0-5): http://www.cmtelecom.com/premium-sms/greece --> <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" /> @@ -169,7 +169,7 @@ <shortcode country="in" pattern="\\d{1,5}" free="59336|53969" /> <!-- Indonesia: 1-5 digits (standard system default, not country specific) --> - <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457|99265" /> + <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457|99265|77413" /> <!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU: http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf --> @@ -226,13 +226,13 @@ <shortcode country="mn" pattern="\\d{1,6}" free="44444|45678|445566" /> <!-- Malawi: 1-5 digits (standard system default, not country specific) --> - <shortcode country="mw" pattern="\\d{1,5}" free="4276" /> + <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305" /> <!-- Mozambique: 1-5 digits (standard system default, not country specific) --> <shortcode country="mz" pattern="\\d{1,5}" free="1714" /> <!-- Mexico: 4-7 digits (not confirmed), known premium codes listed --> - <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303|81811" /> + <shortcode country="mx" pattern="\\d{4,7}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346|3030303|81811|81818" /> <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf --> <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668|66966" /> @@ -324,7 +324,7 @@ <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" /> <!-- Tanzania: 1-5 digits (standard system default, not country specific) --> - <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324" /> + <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324|15610" /> <!-- Tunisia: 5 digits, known premium codes listed --> <shortcode country="tn" pattern="\\d{5}" free="85799" /> @@ -336,11 +336,11 @@ <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" /> <!-- Uganda(UG): 4 digits (standard system default, not country specific) --> - <shortcode country="ug" pattern="\\d{4}" free="8000" /> + <shortcode country="ug" pattern="\\d{4}" free="8000|8009" /> <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm), visual voicemail code for T-Mobile: 122 --> - <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831" /> + <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831|10907" /> <!--Uruguay : 1-6 digits (standard system default, not country specific) --> <shortcode country="uy" pattern="\\d{1,6}" free="55002|191289" /> diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java new file mode 100644 index 000000000000..7afdde244073 --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java @@ -0,0 +1,377 @@ +/* + * 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.hardware.radio; + +import static org.junit.Assert.assertThrows; + +import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; + +import com.google.common.truth.Expect; + +import org.junit.Rule; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +@EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) +public final class RadioAlertUnitTest { + + private static final int TEST_FLAGS = 0; + private static final int CREATOR_ARRAY_SIZE = 3; + private static final String TEST_GEOCODE_VALUE_NAME = "SAME"; + private static final String TEST_GEOCODE_VALUE_1 = "006109"; + private static final String TEST_GEOCODE_VALUE_2 = "006009"; + private static final double TEST_POLYGON_LATITUDE_START = -38.47; + private static final double TEST_POLYGON_LONGITUDE_START = -120.14; + private static final RadioAlert.Coordinate TEST_POLYGON_COORDINATE_START = + new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START); + private static final List<RadioAlert.Coordinate> TEST_COORDINATES = List.of( + TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95), + new RadioAlert.Coordinate(38.52, -119.74), new RadioAlert.Coordinate(38.62, -119.89), + TEST_POLYGON_COORDINATE_START); + private static final RadioAlert.Polygon TEST_POLYGON = new RadioAlert.Polygon(TEST_COORDINATES); + private static final RadioAlert.Geocode TEST_GEOCODE_1 = new RadioAlert.Geocode( + TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1); + private static final RadioAlert.Geocode TEST_GEOCODE_2 = new RadioAlert.Geocode( + TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2); + private static final RadioAlert.AlertArea TEST_AREA_1 = new RadioAlert.AlertArea( + List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1)); + private static final RadioAlert.AlertArea TEST_AREA_2 = new RadioAlert.AlertArea( + new ArrayList<>(), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2)); + + @Rule + public final Expect mExpect = Expect.create(); + + @Test + public void constructor_withNullValueName_forGeocode_fails() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new RadioAlert.Geocode(/* valueName= */ null, TEST_GEOCODE_VALUE_1)); + + mExpect.withMessage("Exception for geocode constructor with null value name") + .that(thrown).hasMessageThat() + .contains("Geocode value name can not be null"); + } + + @Test + public void constructor_withNullValue_forGeocode_fails() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME, /* value= */ null)); + + mExpect.withMessage("Exception for geocode constructor with null value") + .that(thrown).hasMessageThat() + .contains("Geocode value can not be null"); + } + + @Test + public void getValueName_forGeocode() { + mExpect.withMessage("Value name of geocode").that(TEST_GEOCODE_1.getValueName()) + .isEqualTo(TEST_GEOCODE_VALUE_NAME); + } + + @Test + public void getValue_forGeocode() { + mExpect.withMessage("Value of geocode").that(TEST_GEOCODE_1.getValue()) + .isEqualTo(TEST_GEOCODE_VALUE_1); + } + + @Test + public void describeContents_forGeocode() { + mExpect.withMessage("Contents of geocode") + .that(TEST_GEOCODE_1.describeContents()).isEqualTo(0); + } + + @Test + public void writeToParcel_forGeocode() { + Parcel parcel = Parcel.obtain(); + + TEST_GEOCODE_1.writeToParcel(parcel, TEST_FLAGS); + + parcel.setDataPosition(0); + RadioAlert.Geocode geocodeFromParcel = RadioAlert.Geocode.CREATOR.createFromParcel(parcel); + mExpect.withMessage("Geocode from parcel").that(geocodeFromParcel) + .isEqualTo(TEST_GEOCODE_1); + } + + @Test + public void newArray_forGeocodeCreator() { + RadioAlert.Geocode[] geocodes = RadioAlert.Geocode.CREATOR.newArray(CREATOR_ARRAY_SIZE); + + mExpect.withMessage("Geocodes").that(geocodes).hasLength(CREATOR_ARRAY_SIZE); + } + + @Test + public void hashCode_withSameGeocodes() { + RadioAlert.Geocode geocodeCompared = new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME, + TEST_GEOCODE_VALUE_1); + + mExpect.withMessage("Hash code of the same gecode") + .that(geocodeCompared.hashCode()).isEqualTo(TEST_GEOCODE_1.hashCode()); + } + + @Test + public void equals_withDifferentGeocodes() { + mExpect.withMessage("Different geocode").that(TEST_GEOCODE_1) + .isNotEqualTo(TEST_GEOCODE_2); + } + + @Test + @SuppressWarnings("TruthIncompatibleType") + public void equals_withDifferentTypeObject_forGeocode() { + mExpect.withMessage("Non-geocode object").that(TEST_GEOCODE_1) + .isNotEqualTo(TEST_POLYGON_COORDINATE_START); + } + + @Test + public void constructor_withInvalidLatitude_forCoordinate_fails() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> + new RadioAlert.Coordinate(/* latitude= */ -92.0, TEST_POLYGON_LONGITUDE_START)); + + mExpect.withMessage("Exception for coordinate constructor with invalid latitude") + .that(thrown).hasMessageThat().contains("Latitude"); + } + + @Test + public void constructor_withInvalidLongitude_forCoordinate_fails() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> + new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, /* longitude= */ 200.0)); + + mExpect.withMessage("Exception for coordinate constructor with invalid longitude") + .that(thrown).hasMessageThat().contains("Longitude"); + } + + @Test + public void getLatitude_forCoordinate() { + mExpect.withMessage("Latitude of coordinate") + .that(TEST_POLYGON_COORDINATE_START.getLatitude()) + .isEqualTo(TEST_POLYGON_LATITUDE_START); + } + + @Test + public void getLongitude_forCoordinate() { + mExpect.withMessage("Longitude of coordinate") + .that(TEST_POLYGON_COORDINATE_START.getLongitude()) + .isEqualTo(TEST_POLYGON_LONGITUDE_START); + } + + @Test + public void describeContents_forCoordinate() { + mExpect.withMessage("Contents of coordinate") + .that(TEST_POLYGON_COORDINATE_START.describeContents()).isEqualTo(0); + } + + @Test + public void writeToParcel_forCoordinate() { + Parcel parcel = Parcel.obtain(); + + TEST_POLYGON_COORDINATE_START.writeToParcel(parcel, TEST_FLAGS); + + parcel.setDataPosition(0); + RadioAlert.Coordinate coordinateFromParcel = RadioAlert.Coordinate.CREATOR + .createFromParcel(parcel); + mExpect.withMessage("Coordinate from parcel").that(coordinateFromParcel) + .isEqualTo(TEST_POLYGON_COORDINATE_START); + } + + @Test + public void newArray_forCoordinateCreator() { + RadioAlert.Coordinate[] coordinates = RadioAlert.Coordinate.CREATOR + .newArray(CREATOR_ARRAY_SIZE); + + mExpect.withMessage("Coordinates").that(coordinates).hasLength(CREATOR_ARRAY_SIZE); + } + + @Test + public void hashCode_withSameCoordinates() { + RadioAlert.Coordinate coordinateCompared = new RadioAlert.Coordinate( + TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START); + + mExpect.withMessage("Hash code of the same coordinate") + .that(coordinateCompared.hashCode()) + .isEqualTo(TEST_POLYGON_COORDINATE_START.hashCode()); + } + + @Test + public void equals_withDifferentCoordinates() { + mExpect.withMessage("Different coordinate").that(TEST_POLYGON_COORDINATE_START) + .isNotEqualTo(TEST_COORDINATES.get(1)); + } + + @Test + @SuppressWarnings("TruthIncompatibleType") + public void equals_withDifferentTypeObject_forCoordinate() { + mExpect.withMessage("Non-coordinate object").that(TEST_POLYGON_COORDINATE_START) + .isNotEqualTo(TEST_GEOCODE_1); + } + + @Test + public void constructor_withNullCoordinates_forPolygon_fails() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new RadioAlert.Polygon(/* coordinates= */ null)); + + mExpect.withMessage("Exception for polygon constructor with null coordinates") + .that(thrown).hasMessageThat().contains("Coordinates can not be null"); + } + + @Test + public void constructor_withLessThanFourCoordinates_forPolygon_fails() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> + new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START, + TEST_POLYGON_COORDINATE_START))); + + mExpect.withMessage("Exception for polygon constructor with less than four coordinates") + .that(thrown).hasMessageThat().contains("Number of coordinates must be at least 4"); + } + + @Test + public void constructor_withDifferentFirstAndLastCoordinates_forPolygon_fails() { + IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> + new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START, + new RadioAlert.Coordinate(38.34, -119.95), + new RadioAlert.Coordinate(38.52, -119.74), + new RadioAlert.Coordinate(38.62, -119.89)))); + + mExpect.withMessage( + "Exception for polygon constructor with different first and last coordinates") + .that(thrown).hasMessageThat().contains( + "last and first coordinates must be the same"); + } + + @Test + public void getCoordinates_forPolygon() { + mExpect.withMessage("Coordinates in polygon").that(TEST_POLYGON.getCoordinates()) + .containsExactlyElementsIn(TEST_COORDINATES); + } + + @Test + public void describeContents_forPolygon() { + mExpect.withMessage("Contents of polygon") + .that(TEST_POLYGON.describeContents()).isEqualTo(0); + } + + @Test + public void writeToParcel_forPolygon() { + Parcel parcel = Parcel.obtain(); + + TEST_POLYGON.writeToParcel(parcel, TEST_FLAGS); + + parcel.setDataPosition(0); + RadioAlert.Polygon polygonFromParcel = RadioAlert.Polygon.CREATOR.createFromParcel(parcel); + mExpect.withMessage("Polygon from parcel").that(polygonFromParcel) + .isEqualTo(TEST_POLYGON); + } + + @Test + public void newArray_forPolygonCreator() { + RadioAlert.Polygon[] polygons = RadioAlert.Polygon.CREATOR.newArray(CREATOR_ARRAY_SIZE); + + mExpect.withMessage("Polygons").that(polygons).hasLength(CREATOR_ARRAY_SIZE); + } + + @Test + public void hashCode_withSamePolygons() { + RadioAlert.Polygon polygonCompared = new RadioAlert.Polygon(TEST_COORDINATES); + + mExpect.withMessage("Hash code of the same polygon") + .that(polygonCompared.hashCode()).isEqualTo(TEST_POLYGON.hashCode()); + } + + @Test + @SuppressWarnings("TruthIncompatibleType") + public void equals_withDifferentTypeObject_forPolygon() { + mExpect.withMessage("Non-polygon object").that(TEST_POLYGON) + .isNotEqualTo(TEST_GEOCODE_1); + } + + @Test + public void constructor_withNullPolygons_forAlertArea_fails() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new RadioAlert.AlertArea(/* polygons= */ null, List.of(TEST_GEOCODE_1))); + + mExpect.withMessage("Exception for alert area constructor with null polygon list") + .that(thrown).hasMessageThat().contains("Polygons can not be null"); + } + + @Test + public void constructor_withNullGeocodes_forAlertArea_fails() { + NullPointerException thrown = assertThrows(NullPointerException.class, () -> + new RadioAlert.AlertArea(List.of(TEST_POLYGON), /* geocodes= */ null)); + + mExpect.withMessage("Exception for alert area constructor with null geocode list") + .that(thrown).hasMessageThat().contains("Geocodes can not be null"); + } + + @Test + public void getPolygons_forAlertArea() { + mExpect.withMessage("Polygons in alert area").that(TEST_AREA_1.getPolygons()) + .containsExactly(TEST_POLYGON); + } + + @Test + public void getGeocodes_forAlertArea() { + mExpect.withMessage("Polygons in alert area").that(TEST_AREA_2.getGeocodes()) + .containsExactly(TEST_GEOCODE_1, TEST_GEOCODE_2); + } + + @Test + public void describeContents_forAlertArea() { + mExpect.withMessage("Contents of alert area") + .that(TEST_AREA_1.describeContents()).isEqualTo(0); + } + + @Test + public void writeToParcel_forAlertArea() { + Parcel parcel = Parcel.obtain(); + + TEST_AREA_1.writeToParcel(parcel, TEST_FLAGS); + + parcel.setDataPosition(0); + RadioAlert.AlertArea areaFromParcel = RadioAlert.AlertArea.CREATOR.createFromParcel(parcel); + mExpect.withMessage("Alert area from parcel").that(areaFromParcel) + .isEqualTo(TEST_AREA_1); + } + + @Test + public void newArray_forAlertAreaCreator() { + RadioAlert.AlertArea[] alertAreas = RadioAlert.AlertArea.CREATOR + .newArray(CREATOR_ARRAY_SIZE); + + mExpect.withMessage("Alert areas").that(alertAreas).hasLength(CREATOR_ARRAY_SIZE); + } + + @Test + public void hashCode_withSameAlertAreas() { + RadioAlert.AlertArea alertAreaCompared = new RadioAlert.AlertArea(List.of(TEST_POLYGON), + List.of(TEST_GEOCODE_1)); + + mExpect.withMessage("Hash code of the same alert area") + .that(alertAreaCompared.hashCode()).isEqualTo(TEST_AREA_1.hashCode()); + } + + @Test + public void equals_withDifferentAlertAreas() { + mExpect.withMessage("Different alert area").that(TEST_AREA_1).isNotEqualTo(TEST_AREA_2); + } + + @Test + @SuppressWarnings("TruthIncompatibleType") + public void equals_withDifferentTypeObject_forAlertArea() { + mExpect.withMessage("Non-alert-area object").that(TEST_AREA_1) + .isNotEqualTo(TEST_GEOCODE_1); + } +} diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java index 4c3d4e3af99f..7609d0d50f46 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java @@ -31,6 +31,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; @@ -142,10 +144,13 @@ public final class RadioManagerTest { new ProgramSelector.Identifier[]{}, /* vendorIds= */ null); private static final RadioMetadata METADATA = createMetadata(); + private static final RadioAlert HD_ALERT = createRadioAlert(); private static final RadioManager.ProgramInfo DAB_PROGRAM_INFO = createDabProgramInfo(DAB_SELECTOR); private static final RadioManager.ProgramInfo HD_PROGRAM_INFO = createHdProgramInfo( - HD_SELECTOR); + HD_SELECTOR, /* alert= */ null); + private static final RadioManager.ProgramInfo HD_PROGRAM_INFO_WITH_ALERT = createHdProgramInfo( + HD_SELECTOR, HD_ALERT); private static final int EVENT_ANNOUNCEMENT_TYPE = Announcement.TYPE_EVENT; private static final List<Announcement> TEST_ANNOUNCEMENT_LIST = Arrays.asList( @@ -1163,6 +1168,20 @@ public final class RadioManagerTest { } @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void getAlert() { + mExpect.withMessage("Alert in HD program info") + .that(HD_PROGRAM_INFO_WITH_ALERT.getAlert()).isEqualTo(HD_ALERT); + } + + @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void getAlert_withNullAlert() { + mExpect.withMessage("Null alert in HD program info") + .that(HD_PROGRAM_INFO.getAlert()).isNull(); + } + + @Test public void describeContents_forProgramInfo() { mExpect.withMessage("Program info contents") .that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0); @@ -1190,6 +1209,69 @@ public final class RadioManagerTest { } @Test + @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void equals_forProgramInfoWithAlertAndFlagDisabled() { + mExpect.withMessage("Program info with alert and flag disabled") + .that(HD_PROGRAM_INFO_WITH_ALERT).isEqualTo(HD_PROGRAM_INFO); + } + + @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void equals_forProgramInfoWithAlertAndFlagEnabled() { + RadioManager.ProgramInfo sameProgramInfoWithAlert = createHdProgramInfo(HD_SELECTOR, + HD_ALERT); + + mExpect.withMessage("Program info with alert and flag enabled") + .that(HD_PROGRAM_INFO_WITH_ALERT).isEqualTo(sameProgramInfoWithAlert); + } + + @Test + @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void hashcode_forProgramInfoWithAlertAndFlagDisabled() { + mExpect.withMessage("Hash code of program info with alert and flag disabled") + .that(HD_PROGRAM_INFO_WITH_ALERT.hashCode()).isEqualTo(HD_PROGRAM_INFO.hashCode()); + } + + @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void hashcode_forProgramInfoWithAlertAndFlagEnabled() { + RadioManager.ProgramInfo sameProgramInfoWithAlert = createHdProgramInfo(HD_SELECTOR, + HD_ALERT); + + mExpect.withMessage("Hash code of program info with alert and flag enabled") + .that(HD_PROGRAM_INFO_WITH_ALERT.hashCode()) + .isEqualTo(sameProgramInfoWithAlert.hashCode()); + } + + @Test + @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void writeToParcel_forProgramInfoWithAlertAndFlagDisabled() { + Parcel parcel = Parcel.obtain(); + + HD_PROGRAM_INFO_WITH_ALERT.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + RadioManager.ProgramInfo programInfoFromParcel = + RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel); + mExpect.withMessage("Program info created from parcel with alert and flag disabled") + .that(programInfoFromParcel).isEqualTo(HD_PROGRAM_INFO); + } + + @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void writeToParcel_forProgramInfoWithAlertAndFlagEnabled() { + Parcel parcel = Parcel.obtain(); + + HD_PROGRAM_INFO_WITH_ALERT.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + RadioManager.ProgramInfo programInfoFromParcel = + RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel); + mExpect.withMessage("Program info created from parcel with alert and flag enabled") + .that(programInfoFromParcel).isEqualTo(HD_PROGRAM_INFO_WITH_ALERT); + } + + @Test public void equals_withSameProgramInfo_returnsTrue() { RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR); @@ -1210,6 +1292,13 @@ public final class RadioManagerTest { } @Test + @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) + public void equals_withDifferentAlert_returnsFalse() { + mExpect.withMessage("Program info with different alerts") + .that(HD_PROGRAM_INFO).isNotEqualTo(HD_PROGRAM_INFO_WITH_ALERT); + } + + @Test public void listModules_forRadioManager() throws Exception { createRadioManager(); List<RadioManager.ModuleProperties> modules = new ArrayList<>(); @@ -1399,19 +1488,31 @@ public final class RadioManagerTest { return metadataBuilder.putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest").build(); } + private static RadioAlert createRadioAlert() { + RadioAlert.AlertArea alertArea = new RadioAlert.AlertArea(new ArrayList<>(), + List.of(new RadioAlert.Geocode("SAME", "006109"))); + RadioAlert.AlertInfo alertInfo = new RadioAlert.AlertInfo( + new int[]{RadioAlert.CATEGORY_FIRE}, RadioAlert.URGENCY_EXPECTED, + RadioAlert.SEVERITY_MODERATE, RadioAlert.CERTAINTY_OBSERVED, + "alert description", List.of(alertArea), "en-US"); + return new RadioAlert(RadioAlert.STATUS_ACTUAL, RadioAlert.MESSAGE_TYPE_ALERT, + List.of(alertInfo)); + } + private static RadioManager.ProgramInfo createDabProgramInfo(ProgramSelector selector) { return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(), DAB_FREQUENCY_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED), INFO_FLAGS_DAB, SIGNAL_QUALITY, METADATA, /* vendorInfo= */ null); } - private static RadioManager.ProgramInfo createHdProgramInfo(ProgramSelector selector) { + private static RadioManager.ProgramInfo createHdProgramInfo(ProgramSelector selector, + RadioAlert alert) { long frequency = (selector.getPrimaryId().getValue() >> 32); ProgramSelector.Identifier physicallyTunedToId = new ProgramSelector.Identifier( ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, frequency); return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(), physicallyTunedToId, Collections.emptyList(), INFO_FLAGS_HD, SIGNAL_QUALITY, METADATA, - /* vendorInfo= */ null); + /* vendorInfo= */ null, alert); } private void createRadioManager() throws RemoteException { diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp index 53c22df67b85..ae04aa4b5576 100644 --- a/core/tests/FileSystemUtilsTest/Android.bp +++ b/core/tests/FileSystemUtilsTest/Android.bp @@ -69,7 +69,7 @@ java_test_host { "compatibility-host-util", "compatibility-tradefed", ], - data: [ + device_common_data: [ ":embedded_native_libs_test_app", ":extract_native_libs_test_app", ], diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 56e18e6c443f..aee1c3b2f28c 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -146,6 +146,10 @@ android_test { ":BinderProxyCountingTestService", ":AppThatUsesAppOps", ":AppThatCallsBinderMethods", + ":HelloWorldSdk1", + ":HelloWorldUsingSdk1AndSdk1", + ":HelloWorldUsingSdk1And2", + ":HelloWorldUsingSdkMalformedNegativeVersion", ], } diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 05ab783c01bb..3bc81724bc0a 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -29,6 +29,18 @@ <option name="test-file-name" value="AppThatCallsBinderMethods.apk" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true"/> + <option name="push-file" key="HelloWorldUsingSdk1And2.apk" + value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1And2.apk"/> + <option name="push-file" key="HelloWorldUsingSdk1AndSdk1.apk" + value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1AndSdk1.apk"/> + <option name="push-file" key="HelloWorldUsingSdkMalformedNegativeVersion.apk" + value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdkMalformedNegativeVersion.apk"/> + <option name="push-file" key="HelloWorldSdk1.apk" + value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <!-- TODO(b/254155965): Design a mechanism to finally remove this command. --> <option name="run-command" value="settings put global device_config_sync_disabled 0" /> diff --git a/core/tests/coretests/AppThatUsesAppOps/OWNERS b/core/tests/coretests/AppThatUsesAppOps/OWNERS new file mode 100644 index 000000000000..d77457062c13 --- /dev/null +++ b/core/tests/coretests/AppThatUsesAppOps/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/permission/OWNERS diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS index 6aefb638e537..eea694e56363 100644 --- a/core/tests/coretests/OWNERS +++ b/core/tests/coretests/OWNERS @@ -4,4 +4,5 @@ per-file BinderTest.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file SurfaceControlRegistryTests.java = file:/services/core/java/com/android/server/wm/OWNERS per-file VintfObjectTest.java = file:platform/system/libvintf:/OWNERS -per-file AppOpsLoggingTest.kt,AppOpsLoggingTest.cpp,IAppOps*.aidl,AppThatUsesAppOps/* = file:/core/java/android/permission/OWNERS +per-file AppOpsLoggingTest.kt,AppOpsLoggingTest.cpp,IAppOps*.aidl = file:/core/java/android/permission/OWNERS +per-file WallpaperDescriptionTest,WallpaperInstanceTest = file:/core/java/android/service/wallpaper/OWNERS diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 23a09857032c..a2ff4ca0f4a3 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -16,6 +16,7 @@ package android.app; +import static android.app.Notification.Action.EXTRA_DATA_ONLY_INPUTS; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_READ; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_REPLY; import static android.app.Notification.CarExtender.UnreadConversation.KEY_REMOTE_INPUT; @@ -97,6 +98,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.TextAppearanceSpan; import android.util.Pair; +import android.view.View; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; @@ -106,10 +108,10 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; -import junit.framework.Assert; - import libcore.junit.util.compat.CoreCompatChangeRule; +import junit.framework.Assert; + import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; @@ -545,6 +547,22 @@ public class NotificationTest { } @Test + public void largeIconMultipleReferences_ignoreBadData() { + Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( + mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); + + Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build(); + assertSame(n.getLargeIcon(), originalIcon); + n.extras.putParcelable(EXTRA_LARGE_ICON, new NotificationChannelGroup("hi", "hi")); + + Notification q = writeAndReadParcelable(n); + assertNotSame(q.getLargeIcon(), n.getLargeIcon()); + + assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap())); + assertSame(q.getLargeIcon(), q.extras.getParcelable(EXTRA_LARGE_ICON)); + } + + @Test public void largeIconReferenceInExtrasOnly_keptAfterParcelling() { Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); @@ -2444,6 +2462,69 @@ public class NotificationTest { assertThat(progressStyle1.isStyledByProgress()).isTrue(); } + + @Test + public void getDataOnlyRemoteInputs_invalidData() { + Notification.Action action = new Notification.Action.Builder(0, "title", null) + .addRemoteInput(new RemoteInput.Builder("result") + .setAllowFreeFormInput(false) + .setAllowDataType("mimeType", true) + .build()).build(); + action.getExtras().putParcelable(EXTRA_DATA_ONLY_INPUTS, + new NotificationChannelGroup("hi", "hi")); + assertThat(action.getDataOnlyRemoteInputs()).isNull(); + } + + @Test + public void actionAddExtras_invalidData() { + Bundle extras = new Bundle(); + extras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, + new NotificationChannelGroup[]{new NotificationChannelGroup("hi", "hi")}); + Notification.Action action = new Notification.Action.Builder(0, "title", null) + .addRemoteInput(new RemoteInput.Builder("result") + .setAllowFreeFormInput(false) + .setAllowDataType("mimeType", true) + .addExtras(extras) + .build()).build(); + assertThat(action.getDataOnlyRemoteInputs()[0].getClass()).isEqualTo(RemoteInput.class); + } + + @Test + public void makeBigTemplate_invalidData() { + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(mContext, NotificationTest.class), + PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + RemoteInput remoteInput = + new RemoteInput.Builder("key_text_reply") + .setLabel("Reply") + .setChoices(new String[]{"Choice 1", "Choice 2"}) + .addExtras(new Bundle()) + .build(); + Notification.Action replyAction = new Notification.Action.Builder( + R.drawable.stat_notify_chat, "Reply", pendingIntent) + .addRemoteInput(remoteInput) + .build(); + + Bundle bundle = new Bundle(); + bundle.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + new NotificationChannelGroup[]{}); + + Notification.Builder nb = new Notification.Builder(mContext, "channel") + .setContentTitle("title") + .setVisibility(android.app.Notification.VISIBILITY_PUBLIC) + .setStyle(new Notification.InboxStyle()) + .setExtras(bundle) + .setSmallIcon(R.drawable.stat_notify_chat) + .addAction(replyAction); + + RemoteViews views = nb.createBigContentView(); + View view = views.apply(mContext, null); + assertThat(view.findViewById(R.id.notification_material_reply_container).getVisibility()) + .isNotEqualTo(View.VISIBLE); + assertThat(view.findViewById(R.id.inbox_text0).getVisibility()) + .isNotEqualTo(View.VISIBLE); + } + private void assertValid(Notification.Colors c) { // Assert that all colors are populated assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID); diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index dcea5b299829..65153f55295a 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -16,13 +16,23 @@ package android.app; +import static android.app.PropertyInvalidatedCache.NONCE_UNSET; +import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX; +import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.android.internal.os.ApplicationSharedMemory; + import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; @@ -47,6 +57,9 @@ public class PropertyInvalidatedCacheTests { @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule(); + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + // Configuration for creating caches private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST; private static final String API = "testApi"; @@ -423,4 +436,54 @@ public class PropertyInvalidatedCacheTests { // Re-enable test mode (so that the cleanup for the test does not throw). PropertyInvalidatedCache.setTestMode(true); } + + // Verify the behavior of shared memory nonce storage. This does not directly test the cache + // storing nonces in shared memory. + @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED) + @Test + public void testSharedMemoryStorage() { + // Fetch a shared memory instance for testing. + ApplicationSharedMemory shmem = ApplicationSharedMemory.create(); + + // Create a server-side store and a client-side store. The server's store is mutable and + // the client's store is not mutable. + PropertyInvalidatedCache.NonceStore server = + new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true); + PropertyInvalidatedCache.NonceStore client = + new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false); + + final String name1 = "name1"; + assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX); + assertEquals(client.getHandleForName(name1), INVALID_NONCE_INDEX); + final int index1 = server.storeName(name1); + assertNotEquals(index1, INVALID_NONCE_INDEX); + assertEquals(server.getHandleForName(name1), index1); + assertEquals(client.getHandleForName(name1), index1); + assertEquals(server.storeName(name1), index1); + + assertEquals(server.getNonce(index1), NONCE_UNSET); + assertEquals(client.getNonce(index1), NONCE_UNSET); + final int value1 = 4; + server.setNonce(index1, value1); + assertEquals(server.getNonce(index1), value1); + assertEquals(client.getNonce(index1), value1); + final int value2 = 8; + server.setNonce(index1, value2); + assertEquals(server.getNonce(index1), value2); + assertEquals(client.getNonce(index1), value2); + + final String name2 = "name2"; + assertEquals(server.getHandleForName(name2), INVALID_NONCE_INDEX); + assertEquals(client.getHandleForName(name2), INVALID_NONCE_INDEX); + final int index2 = server.storeName(name2); + assertNotEquals(index2, INVALID_NONCE_INDEX); + assertEquals(server.getHandleForName(name2), index2); + assertEquals(client.getHandleForName(name2), index2); + assertEquals(server.storeName(name2), index2); + + // The names are different, so the indices must be different. + assertNotEquals(index1, index2); + + shmem.close(); + } } diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java new file mode 100644 index 000000000000..f9b481ffc1ef --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java @@ -0,0 +1,301 @@ +/* + * 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.content.pm.parsing; + +import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES; +import static android.content.pm.PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.SuppressLint; +import android.app.UiAutomation; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.os.FileUtils; +import android.os.ParcelFileDescriptor; +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; +import android.util.PackageUtils; + +import androidx.annotation.NonNull; +import androidx.test.InstrumentationRegistry; + +import com.android.internal.pm.parsing.PackageParser2; +import com.android.server.pm.pkg.AndroidPackage; + +import libcore.util.HexEncoding; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +@Presubmit +public class ApkLiteParseUtilsTest { + + @Rule + public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + + private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/coretests/pm/"; + private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk"; + private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk"; + private static final String TEST_APP_USING_SDK_MALFORMED_VERSION = + "HelloWorldUsingSdkMalformedNegativeVersion.apk"; + private static final String TEST_SDK1 = "HelloWorldSdk1.apk"; + private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1"; + private static final String TEST_SDK1_NAME = "com.test.sdk1"; + private static final long TEST_SDK1_VERSION = 1; + private static final String TEST_SDK2_NAME = "com.test.sdk2"; + private static final long TEST_SDK2_VERSION = 2; + + private final PackageParser2 mPackageParser2 = new PackageParser2( + null, null, null, new FakePackageParser2Callback()); + + private File mTmpDir = null; + + @Before + public void setUp() throws IOException { + mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest"); + } + + @After + public void tearDown() throws Exception { + setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", "invalid"); + uninstallPackageSilently(TEST_SDK1_NAME); + } + + @SuppressLint("CheckResult") + @Test + public void testParseApkLite_getUsesSdkLibrary() throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isFalse(); + + ApkLite baseApk = result.getResult(); + assertThat(baseApk.getUsesSdkLibraries()).containsExactly(TEST_SDK1_NAME, TEST_SDK2_NAME); + assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly( + TEST_SDK1_VERSION, TEST_SDK2_VERSION + ); + for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) { + assertThat(certDigests).asList().containsExactly(""); + } + } + + @SuppressLint("CheckResult") + @Test + public void testParseApkLite_getUsesSdkLibrary_overrideCertDigest() throws Exception { + installPackage(TEST_SDK1); + String certDigest = getPackageCertDigest(TEST_SDK1_PACKAGE); + overrideUsesSdkLibraryCertificateDigest(certDigest); + + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + ApkLite baseApk = result.getResult(); + + String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests(); + assertThat(liteCerts).isNotNull(); + for (String[] certDigests: liteCerts) { + assertThat(certDigests).asList().containsExactly(certDigest); + } + + // Same for package parser + AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal(); + String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests(); + assertThat(pkgCerts).isNotNull(); + for (int i = 0; i < liteCerts.length; i++) { + assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]); + } + } + + + @SuppressLint("CheckResult") + @Test + public void testParseApkLite_getUsesSdkLibrary_sameAsPackageParser() throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isFalse(); + ApkLite baseApk = result.getResult(); + + AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal(); + assertThat(baseApk.getUsesSdkLibraries()) + .containsExactlyElementsIn(pkg.getUsesSdkLibraries()); + List<Long> versionsBoxed = Arrays.stream(pkg.getUsesSdkLibrariesVersionsMajor()).boxed() + .toList(); + assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList() + .containsExactlyElementsIn(versionsBoxed); + + String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests(); + String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests(); + for (int i = 0; i < liteCerts.length; i++) { + assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]); + } + } + + @SuppressLint("CheckResult") + @Test + public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK1); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isTrue(); + assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration"); + assertThat(result.getErrorMessage()).contains( + "Depending on multiple versions of SDK library"); + } + + @SuppressLint("CheckResult") + @Test + public void testParseApkLite_malformedUsesSdkLibrary_missingVersion() throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK_MALFORMED_VERSION); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isTrue(); + assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration"); + } + + private String getPackageCertDigest(String packageName) throws Exception { + getUiAutomation().adoptShellPermissionIdentity(); + try { + PackageInfo sdkPackageInfo = getPackageManager().getPackageInfo(packageName, + PackageManager.PackageInfoFlags.of( + GET_SIGNING_CERTIFICATES | MATCH_STATIC_SHARED_AND_SDK_LIBRARIES)); + SigningInfo signingInfo = sdkPackageInfo.signingInfo; + Signature[] signatures = + signingInfo != null ? signingInfo.getSigningCertificateHistory() : null; + byte[] digest = PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray()); + return new String(HexEncoding.encode(digest)); + } finally { + getUiAutomation().dropShellPermissionIdentity(); + } + } + + private static PackageManager getPackageManager() { + return InstrumentationRegistry.getContext().getPackageManager(); + } + + /** + * SDK package is signed by build system. In theory we could try to extract the signature, + * and patch the app manifest. This property allows us to override in runtime, which is much + * easier. + */ + private void overrideUsesSdkLibraryCertificateDigest(String sdkCertDigest) throws Exception { + setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", sdkCertDigest); + } + + private void setSystemProperty(String name, String value) throws Exception { + assertThat(executeShellCommand("setprop " + name + " " + value)).isEmpty(); + } + + private static String executeShellCommand(String command) throws IOException { + final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command); + try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) { + return readFullStream(inputStream); + } + } + + private static String readFullStream(InputStream inputStream) throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + writeFullStream(inputStream, result, -1); + return result.toString("UTF-8"); + } + + private static void writeFullStream(InputStream inputStream, OutputStream outputStream, + long expected) throws IOException { + byte[] buffer = new byte[1024]; + long total = 0; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + total += length; + } + if (expected > 0) { + assertThat(expected).isEqualTo(total); + } + } + + private static UiAutomation getUiAutomation() { + return InstrumentationRegistry.getInstrumentation().getUiAutomation(); + } + + private File copyApkToTmpDir(String apkFileName) throws Exception { + File outFile = new File(mTmpDir, apkFileName); + String apkFilePath = PUSH_FILE_DIR + apkFileName; + File apkFile = new File(apkFilePath); + assertThat(apkFile.exists()).isTrue(); + try (InputStream is = new FileInputStream(apkFile)) { + FileUtils.copyToFileOrThrow(is, outFile); + } + return outFile; + } + + static String createApkPath(String baseName) { + return PUSH_FILE_DIR + baseName; + } + + /* Install for all the users */ + private void installPackage(String baseName) throws IOException { + File file = new File(createApkPath(baseName)); + assertThat(executeShellCommand("pm install -t -g " + file.getPath())) + .isEqualTo("Success\n"); + } + + private static String uninstallPackageSilently(String packageName) throws IOException { + return executeShellCommand("pm uninstall " + packageName); + } + + static class FakePackageParser2Callback extends PackageParser2.Callback { + + @Override + public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) { + return true; + } + + @Override + public boolean hasFeature(String feature) { + return true; + } + + @Override + public @NonNull Set<String> getHiddenApiWhitelistedApps() { + return new ArraySet<>(); + } + + @Override + public @NonNull Set<String> getInstallConstraintsAllowlist() { + return new ArraySet<>(); + } + } +} diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java index 90ae306952fe..f1e1df5ae3fb 100644 --- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java +++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java @@ -142,10 +142,10 @@ public class VerificationSessionTest { new VerificationStatus.Builder().setVerified(true).build(); mTestSession.reportVerificationComplete(status); verify(mTestSessionInterface, times(1)).reportVerificationComplete( - eq(TEST_ID), eq(status)); + eq(TEST_ID), eq(status), eq(null)); mTestSession.reportVerificationComplete(status, response); verify(mTestSessionInterface, times(1)) - .reportVerificationCompleteWithExtensionResponse( + .reportVerificationComplete( eq(TEST_ID), eq(status), eq(response)); final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN; diff --git a/core/tests/coretests/src/android/service/settings/OWNERS b/core/tests/coretests/src/android/service/settings/OWNERS new file mode 100644 index 000000000000..abd8ab011b90 --- /dev/null +++ b/core/tests/coretests/src/android/service/settings/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/core/java/android/service/settings/OWNERS diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 46bd73e316f6..4d6c30ebbe2b 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -686,7 +686,7 @@ public class WindowOnBackInvokedDispatcherTest { return new BackMotionEvent( /* touchX = */ 0, /* touchY = */ 0, - /* frameTime = */ 0, + /* frameTimeMillis = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 1cbc7d6d3f98..60b5a422ea80 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -16,9 +16,9 @@ package com.android.internal.jank; -import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED; +import static android.view.SurfaceControl.JankData.JANK_APPLICATION; +import static android.view.SurfaceControl.JankData.JANK_COMPOSER; import static android.view.SurfaceControl.JankData.JANK_NONE; -import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.jank.FrameTracker.ViewRootWrapper; @@ -164,7 +164,7 @@ public class FrameTrackerTest { verify(mRenderer, only()).addObserver(any()); // send first frame with a long duration - should not be taken into account - sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L); + sendFirstWindowFrame(tracker, 100, JANK_APPLICATION, 100L); // send another frame with a short duration - should not be considered janky sendFrame(tracker, 5, JANK_NONE, 101L); @@ -173,7 +173,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(102L); tracker.end(FrameTracker.REASON_END_NORMAL); sendFrame(tracker, 5, JANK_NONE, 102L); - sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L); + sendFrame(tracker, 500, JANK_APPLICATION, 103L); verify(tracker).removeObservers(); verify(mTrackerListener, never()).triggerPerfetto(any()); @@ -202,7 +202,7 @@ public class FrameTrackerTest { sendFrame(tracker, 4, JANK_NONE, 100L); // send another frame - should be considered janky - sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); + sendFrame(tracker, 40, JANK_COMPOSER, 101L); // end the trace session when(mChoreographer.getVsyncId()).thenReturn(102L); @@ -236,7 +236,7 @@ public class FrameTrackerTest { verify(mRenderer, only()).addObserver(any()); // send first frame - janky - sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L); + sendFrame(tracker, 40, JANK_APPLICATION, 100L); // send another frame - not jank sendFrame(tracker, 4, JANK_NONE, 101L); @@ -275,7 +275,7 @@ public class FrameTrackerTest { sendFrame(tracker, 4, JANK_NONE, 100L); // send another frame - should be considered janky - sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L); + sendFrame(tracker, 40, JANK_APPLICATION, 101L); // end the trace session when(mChoreographer.getVsyncId()).thenReturn(102L); @@ -317,7 +317,7 @@ public class FrameTrackerTest { // end the trace session, simulate one more valid callback came after the end call. when(mChoreographer.getVsyncId()).thenReturn(102L); tracker.end(FrameTracker.REASON_END_NORMAL); - sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L); + sendFrame(tracker, 50, JANK_APPLICATION, 102L); // One more callback with VSYNC after the end() vsync id. sendFrame(tracker, 4, JANK_NONE, 103L); @@ -365,7 +365,7 @@ public class FrameTrackerTest { sendSfFrame(tracker, 4, 102L, JANK_NONE); // Send janky but complete callbck fo 103L - sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L); + sendFrame(tracker, 50, JANK_APPLICATION, 103L); verify(tracker).removeObservers(); verify(mTrackerListener, never()).triggerPerfetto(any()); @@ -397,7 +397,7 @@ public class FrameTrackerTest { sendFrame(tracker, 4, JANK_NONE, 101L); // a janky frame - sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L); + sendFrame(tracker, 50, JANK_APPLICATION, 102L); tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL); verify(tracker).removeObservers(); @@ -481,7 +481,7 @@ public class FrameTrackerTest { // normal frame - not janky sendFrame(tracker, JANK_NONE, 101L); // a janky frame - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L); + sendFrame(tracker, JANK_APPLICATION, 102L); when(mChoreographer.getVsyncId()).thenReturn(102L); tracker.end(FrameTracker.REASON_CANCEL_NORMAL); @@ -514,7 +514,7 @@ public class FrameTrackerTest { verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); // First frame - janky - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L); + sendFrame(tracker, JANK_APPLICATION, 100L); // normal frame - not janky sendFrame(tracker, JANK_NONE, 101L); // normal frame - not janky @@ -561,7 +561,7 @@ public class FrameTrackerTest { tracker.end(FrameTracker.REASON_CANCEL_NORMAL); // janky frame, should be ignored, trigger finish - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L); + sendFrame(tracker, JANK_APPLICATION, 103L); verify(mJankStatsRegistration).removeAfter(anyLong()); verify(mTrackerListener, never()).triggerPerfetto(any()); @@ -623,16 +623,16 @@ public class FrameTrackerTest { tracker.begin(); mRunnableArgumentCaptor.getValue().run(); verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); - sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L); - sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L); + sendFrame(tracker, JANK_COMPOSER, 100L); + sendFrame(tracker, JANK_COMPOSER, 101L); + sendFrame(tracker, JANK_APPLICATION, 102L); sendFrame(tracker, JANK_NONE, 103L); - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L); - sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L); + sendFrame(tracker, JANK_APPLICATION, 104L); + sendFrame(tracker, JANK_APPLICATION, 105L); when(mChoreographer.getVsyncId()).thenReturn(106L); tracker.end(FrameTracker.REASON_END_NORMAL); - sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L); - sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L); + sendFrame(tracker, JANK_COMPOSER, 106L); + sendFrame(tracker, JANK_COMPOSER, 107L); verify(mJankStatsRegistration).removeAfter(anyLong()); verify(mTrackerListener).triggerPerfetto(any()); verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED), diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java index 79a478a7676b..35765a9f8e08 100644 --- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java @@ -41,9 +41,26 @@ public class NotificationProgressBarTest { List<ProgressStyle.Segment> segments = new ArrayList<>(); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, + isStyledByProgress); + } + + @Test(expected = IllegalArgumentException.class) + public void processAndConvertToDrawableParts_segmentsLengthNotMatchingProgressMax() { + List<ProgressStyle.Segment> segments = new ArrayList<>(); + segments.add(new ProgressStyle.Segment(50)); + segments.add(new ProgressStyle.Segment(100)); + List<ProgressStyle.Point> points = new ArrayList<>(); + int progress = 50; + int progressMax = 100; + boolean isStyledByProgress = true; + + NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -51,11 +68,14 @@ public class NotificationProgressBarTest { public void processAndConvertToDrawableParts_segmentLengthIsNegative() { List<ProgressStyle.Segment> segments = new ArrayList<>(); segments.add(new ProgressStyle.Segment(-50)); + segments.add(new ProgressStyle.Segment(150)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -63,11 +83,14 @@ public class NotificationProgressBarTest { public void processAndConvertToDrawableParts_segmentLengthIsZero() { List<ProgressStyle.Segment> segments = new ArrayList<>(); segments.add(new ProgressStyle.Segment(0)); + segments.add(new ProgressStyle.Segment(100)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -77,9 +100,11 @@ public class NotificationProgressBarTest { segments.add(new ProgressStyle.Segment(100)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = -50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -89,10 +114,11 @@ public class NotificationProgressBarTest { segments.add(new ProgressStyle.Segment(100).setColor(Color.RED)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 0; + int progressMax = 100; boolean isStyledByProgress = true; List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); int fadedRed = 0x7FFF0000; List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true))); @@ -106,10 +132,11 @@ public class NotificationProgressBarTest { segments.add(new ProgressStyle.Segment(100).setColor(Color.RED)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 100; + int progressMax = 100; boolean isStyledByProgress = true; List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED))); @@ -122,10 +149,11 @@ public class NotificationProgressBarTest { segments.add(new ProgressStyle.Segment(100)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 150; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, - isStyledByProgress); + progressMax, isStyledByProgress); } @Test(expected = IllegalArgumentException.class) @@ -135,9 +163,11 @@ public class NotificationProgressBarTest { List<ProgressStyle.Point> points = new ArrayList<>(); points.add(new ProgressStyle.Point(-50).setColor(Color.RED)); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -148,9 +178,11 @@ public class NotificationProgressBarTest { List<ProgressStyle.Point> points = new ArrayList<>(); points.add(new ProgressStyle.Point(0).setColor(Color.RED)); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -161,9 +193,11 @@ public class NotificationProgressBarTest { List<ProgressStyle.Point> points = new ArrayList<>(); points.add(new ProgressStyle.Point(100).setColor(Color.RED)); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -174,9 +208,11 @@ public class NotificationProgressBarTest { List<ProgressStyle.Point> points = new ArrayList<>(); points.add(new ProgressStyle.Point(150).setColor(Color.RED)); int progress = 50; + int progressMax = 100; boolean isStyledByProgress = true; NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress, + progressMax, isStyledByProgress); } @@ -187,10 +223,11 @@ public class NotificationProgressBarTest { segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN)); List<ProgressStyle.Point> points = new ArrayList<>(); int progress = 60; + int progressMax = 100; boolean isStyledByProgress = true; List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); // Colors with 50% opacity int fadedGreen = 0x7F00FF00; @@ -213,6 +250,7 @@ public class NotificationProgressBarTest { points.add(new ProgressStyle.Point(60).setColor(Color.BLUE)); points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); int progress = 60; + int progressMax = 100; boolean isStyledByProgress = true; // Colors with 50% opacity @@ -231,7 +269,7 @@ public class NotificationProgressBarTest { new Segment(0.25f, fadedBlue, true))); List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); assertThat(parts).isEqualTo(expected); } @@ -247,10 +285,11 @@ public class NotificationProgressBarTest { points.add(new ProgressStyle.Point(60).setColor(Color.BLUE)); points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); int progress = 60; + int progressMax = 100; boolean isStyledByProgress = true; List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); // Colors with 50% opacity int fadedGreen = 0x7F00FF00; @@ -281,10 +320,11 @@ public class NotificationProgressBarTest { points.add(new ProgressStyle.Point(25).setColor(Color.BLUE)); points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW)); int progress = 60; + int progressMax = 100; boolean isStyledByProgress = false; List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts( - segments, points, progress, isStyledByProgress); + segments, points, progress, progressMax, isStyledByProgress); List<Part> expected = new ArrayList<>(List.of( new Segment(0.15f, Color.RED), diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp index 0a6b88bcbb63..31c15148d8d8 100644 --- a/core/tests/overlaytests/remount/Android.bp +++ b/core/tests/overlaytests/remount/Android.bp @@ -32,7 +32,7 @@ java_test_host { "frameworks-base-hostutils", ], test_suites: ["general-tests"], - java_resources: [ + device_common_java_resources: [ ":com.android.overlaytest.overlaid", ":com.android.overlaytest.overlay", ":OverlayRemountedTest_SharedLibrary", diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java index 04945f38e319..9099918edb02 100644 --- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java +++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java @@ -71,8 +71,7 @@ public class VibratorInfoTest { VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); assertFalse(noCapabilities.hasFrequencyControl()); VibratorInfo composeAndFrequencyControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities( - IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS) + .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL) .build(); assertTrue(composeAndFrequencyControl.hasFrequencyControl()); } @@ -153,7 +152,8 @@ public class VibratorInfoTest { VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); assertFalse(noCapabilities.areEnvelopeEffectsSupported()); VibratorInfo envelopeEffectCapability = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2) + .setCapabilities( + IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2) .build(); assertTrue(envelopeEffectCapability.areEnvelopeEffectsSupported()); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index a028e1829ac6..debd0df95cdf 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -659,5 +659,6 @@ applications that come with the platform <privapp-permissions package="com.android.devicediagnostics"> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.BATTERY_STATS"/> + <permission name="android.permission.ENTER_TRADE_IN_MODE"/> </privapp-permissions> </permissions> diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index cb3b64c3e6cd..93d94c9cd7eb 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -16,6 +16,7 @@ package android.graphics; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import java.lang.annotation.Retention; @@ -41,6 +42,7 @@ public class ImageFormat { Y8, Y16, YCBCR_P010, + YCBCR_P210, NV16, NV21, YUY2, @@ -206,6 +208,26 @@ public class ImageFormat { public static final int YCBCR_P010 = 0x36; /** + * <p>Android YUV P210 format.</p> + * + * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a WxH CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. + * + * <p>For example, the {@link android.media.Image} object can provide data + * in this format from a {@link android.hardware.camera2.CameraDevice} + * through a {@link android.media.ImageReader} object if this format is + * supported by {@link android.hardware.camera2.CameraDevice}.</p> + * + * @see android.media.Image + * @see android.media.ImageReader + * @see android.hardware.camera2.CameraDevice + * + */ + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int YCBCR_P210 = 0x3c; + + /** * YCbCr format, used for video. * * <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is @@ -849,6 +871,8 @@ public class ImageFormat { return 16; case YCBCR_P010: return 24; + case YCBCR_P210: + return 32; case RAW_DEPTH10: case RAW10: return 10; @@ -899,7 +923,9 @@ public class ImageFormat { case JPEG_R: return true; } - + if (android.media.codec.Flags.p210FormatSupport() && format == YCBCR_P210) { + return true; + } return false; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java index 85c4fe1193ee..252974dd474e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/layout/CommonFoldingFeature.java @@ -25,6 +25,7 @@ import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -212,7 +213,8 @@ public final class CommonFoldingFeature { @NonNull private final Rect mRect; - CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { + @VisibleForTesting + public CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) { assertReportableState(state); this.mType = type; this.mState = state; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 2ab0310d6789..fcf3a3759f7a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -57,6 +57,8 @@ class WindowExtensionsImpl implements WindowExtensions { */ private static final int NO_LEVEL_OVERRIDE = -1; + private static final int EXTENSIONS_VERSION_V9 = 9; + private static final int EXTENSIONS_VERSION_V8 = 8; private static final int EXTENSIONS_VERSION_V7 = 7; @@ -80,6 +82,9 @@ class WindowExtensionsImpl implements WindowExtensions { */ @VisibleForTesting static int getExtensionsVersionCurrentPlatform() { + if (Flags.wlinfoOncreate()) { + return EXTENSIONS_VERSION_V9; + } if (Flags.aeBackStackRestore()) { return EXTENSIONS_VERSION_V8; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index a3d2d7f4dcdf..438532725686 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -16,6 +16,10 @@ package androidx.window.extensions.area; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; import android.app.Activity; @@ -23,6 +27,7 @@ import android.content.Context; import android.hardware.devicestate.DeviceState; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateRequest; +import android.hardware.devicestate.feature.flags.Flags; import android.hardware.display.DisplayManager; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -72,18 +77,18 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private final ArraySet<Consumer<ExtensionWindowAreaStatus>> mRearDisplayPresentationStatusListeners = new ArraySet<>(); - private final int mRearDisplayState; + private int mRearDisplayState = INVALID_DEVICE_STATE_IDENTIFIER; private final int mConcurrentDisplayState; @NonNull - private final int[] mFoldedDeviceStates; + private int[] mFoldedDeviceStates = new int[0]; private long mRearDisplayAddress = INVALID_DISPLAY_ADDRESS; @WindowAreaSessionState private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE; @GuardedBy("mLock") - private int mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER; + private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; @GuardedBy("mLock") - private int[] mCurrentSupportedDeviceStates; + private List<DeviceState> mCurrentSupportedDeviceStates; @GuardedBy("mLock") private DeviceStateRequest mRearDisplayStateRequest; @@ -103,16 +108,25 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mDisplayManager = context.getSystemService(DisplayManager.class); mExecutor = context.getMainExecutor(); - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers( - mDeviceStateManager.getSupportedDeviceStates()); - mFoldedDeviceStates = context.getResources().getIntArray( - R.array.config_foldedDeviceStates); + mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates(); - // TODO(b/236022708) Move rear display state to device state config file - mRearDisplayState = context.getResources().getInteger( - R.integer.config_deviceStateRearDisplay); + if (Flags.deviceStatePropertyMigration()) { + for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) { + DeviceState state = mCurrentSupportedDeviceStates.get(i); + if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) { + mRearDisplayState = state.getIdentifier(); + break; + } + } + } else { + mFoldedDeviceStates = context.getResources().getIntArray( + R.array.config_foldedDeviceStates); + // TODO(b/236022708) Move rear display state to device state config file + mRearDisplayState = context.getResources().getInteger( + R.integer.config_deviceStateRearDisplay); + } + // TODO(b/374351956) Use DeviceState API when the dual display state is always returned mConcurrentDisplayState = context.getResources().getInteger( R.integer.config_deviceStateConcurrentRearDisplay); @@ -147,7 +161,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided. - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } consumer.accept(getCurrentRearDisplayModeStatus()); @@ -312,7 +326,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mRearDisplayPresentationStatusListeners.add(consumer); // If current device state is still invalid, the initial value has not been provided - if (mCurrentDeviceState == INVALID_DEVICE_STATE_IDENTIFIER) { + if (mCurrentDeviceState.getIdentifier() == INVALID_DEVICE_STATE_IDENTIFIER) { return; } @WindowAreaStatus int currentStatus = getCurrentRearDisplayPresentationModeStatus(); @@ -452,8 +466,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(supportedStates); + mCurrentSupportedDeviceStates = supportedStates; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -463,8 +476,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @Override public void onDeviceStateChanged(@NonNull DeviceState state) { synchronized (mLock) { - // TODO(b/329436166): Update the usage of device state manager API's - mCurrentDeviceState = state.getIdentifier(); + mCurrentDeviceState = state; updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus()); updateRearDisplayPresentationStatusListeners( getCurrentRearDisplayPresentationModeStatus()); @@ -477,7 +489,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mRearDisplayState)) { return WindowAreaComponent.STATUS_UNAVAILABLE; } @@ -488,15 +501,6 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_AVAILABLE; } - // TODO(b/329436166): Remove and update the usage of device state manager API's - private int[] getSupportedStateIdentifiers(@NonNull List<DeviceState> states) { - int[] identifiers = new int[states.size()]; - for (int i = 0; i < states.size(); i++) { - identifiers[i] = states.get(i).getIdentifier(); - } - return identifiers; - } - /** * Helper method to determine if a rear display session is currently active by checking * if the current device state is that which corresponds to {@code mRearDisplayState}. @@ -505,7 +509,31 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, */ @GuardedBy("mLock") private boolean isRearDisplayActive() { - return mCurrentDeviceState == mRearDisplayState; + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY); + } else { + return mCurrentDeviceState.getIdentifier() == mRearDisplayState; + } + } + + @GuardedBy("mLock") + private boolean isRearDisplayPresentationModeActive() { + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty(PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT); + } else { + return mCurrentDeviceState.getIdentifier() == mConcurrentDisplayState; + } + } + + @GuardedBy("mLock") + private boolean deviceStateListContainsIdentifier(List<DeviceState> deviceStates, + int identifier) { + for (int i = 0; i < deviceStates.size(); i++) { + if (deviceStates.get(i).getIdentifier() == identifier) { + return true; + } + } + return false; } @GuardedBy("mLock") @@ -526,12 +554,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, return WindowAreaComponent.STATUS_UNSUPPORTED; } - if (mCurrentDeviceState == mConcurrentDisplayState) { + if (isRearDisplayPresentationModeActive()) { return WindowAreaComponent.STATUS_ACTIVE; } - if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState) - || isDeviceFolded()) { + if (!deviceStateListContainsIdentifier(mCurrentSupportedDeviceStates, + mConcurrentDisplayState) || isDeviceFolded()) { return WindowAreaComponent.STATUS_UNAVAILABLE; } return WindowAreaComponent.STATUS_AVAILABLE; @@ -539,7 +567,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private boolean isDeviceFolded() { - return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState); + if (Flags.deviceStatePropertyApi()) { + return mCurrentDeviceState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); + } else { + return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState.getIdentifier()); + } } @GuardedBy("mLock") diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 60e1a506ab73..4c7e47769613 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -2314,15 +2314,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") @Nullable Bundle getPlaceholderOptions(@NonNull Activity primaryActivity, boolean isOnCreated) { - // Setting avoid move to front will also skip the animation. We only want to do that when - // the Task is currently in background. // Check if the primary is resumed or if this is called when the primary is onCreated // (not resumed yet). if (isOnCreated || primaryActivity.isResumed()) { // Only set trigger type if the launch happens in foreground. mTransactionManager.getCurrentTransactionRecord() .setOriginType(TASK_FRAGMENT_TRANSIT_OPEN); - return null; } final ActivityOptions options = ActivityOptions.makeBasic(); options.setAvoidMoveToFront(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index f1ea19a60f97..556da3798df5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -74,6 +74,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @GuardedBy("mLock") private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; + /** + * The last reported folding features from the device. This is initialized in the constructor + * because the data change callback added to {@link #mFoldingFeatureProducer} is immediately + * called. This is due to current device state from the device state manager already being + * available in the {@link DeviceStateManagerFoldingFeatureProducer}. + */ @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); @@ -308,6 +314,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * @param context a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. */ + @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) { List<DisplayFeature> displayFeatureList = getDisplayFeatures(context, storedFeatures); @@ -329,6 +336,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + @Override + @NonNull + public WindowLayoutInfo getCurrentWindowLayoutInfo(@NonNull @UiContext Context context) { + synchronized (mLock) { + return getWindowLayoutInfo(context, mLastReportedFoldingFeatures); + } + } + /** * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over * time. diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 05124121fe7b..b8a36eb4d3f5 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -166,6 +166,8 @@ public class SplitControllerTest { private List<SplitInfo> mSplitInfos; private TransactionManager mTransactionManager; private ActivityThread mCurrentActivityThread; + private final ArgumentCaptor<Bundle> mBundleArgumentCaptor = + ArgumentCaptor.forClass(Bundle.class); @Before public void setUp() { @@ -685,9 +687,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -720,9 +726,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -755,9 +765,13 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT, - mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */), - placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */); + verify(mSplitPresenter).startActivityToSide(eq(mTransaction), eq(mActivity), + eq(PLACEHOLDER_INTENT), mBundleArgumentCaptor.capture(), + eq(placeholderRule), eq(SPLIT_ATTRIBUTES), eq(true) /* isPlaceholder */); + + final ActivityOptions activityOptions = + new ActivityOptions(mBundleArgumentCaptor.getValue()); + assertTrue(activityOptions.getAvoidMoveToFront()); } @Test @@ -1065,16 +1079,8 @@ public class SplitControllerTest { public void testGetPlaceholderOptions() { // Setup to make sure a transaction record is started. mTransactionManager.startNewTransaction(); - doReturn(true).when(mActivity).isResumed(); - - assertNull(mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */)); - - doReturn(false).when(mActivity).isResumed(); - - assertNull(mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */)); - // Launch placeholder without moving the Task to front if the Task is now in background (not - // resumed or onCreated). + // Launch placeholder without moving the Task to front final Bundle options = mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java index ff0a82fe05d6..ed4eddf7c209 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/layout/WindowLayoutComponentImplTest.java @@ -16,22 +16,30 @@ package androidx.window.extensions.layout; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; +import android.app.WindowConfiguration; import android.content.Context; import android.content.ContextWrapper; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.common.layout.CommonFoldingFeature; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collections; +import java.util.List; /** * Test class for {@link WindowLayoutComponentImpl}. @@ -44,31 +52,91 @@ import java.util.Collections; @RunWith(AndroidJUnit4.class) public class WindowLayoutComponentImplTest { + private final Context mAppContext = ApplicationProvider.getApplicationContext(); + + @NonNull private WindowLayoutComponentImpl mWindowLayoutComponent; @Before public void setUp() { - mWindowLayoutComponent = new WindowLayoutComponentImpl( - ApplicationProvider.getApplicationContext(), + mWindowLayoutComponent = new WindowLayoutComponentImpl(mAppContext, mock(DeviceStateManagerFoldingFeatureProducer.class)); } @Test - public void testAddWindowLayoutListenerOnFakeUiContext_noCrash() { - final Context fakeUiContext = createTestContext(); + public void testAddWindowLayoutListener_onFakeUiContext_noCrash() { + final Context fakeUiContext = new FakeUiContext(mAppContext); mWindowLayoutComponent.addWindowLayoutInfoListener(fakeUiContext, info -> {}); mWindowLayoutComponent.onDisplayFeaturesChanged(Collections.emptyList()); } - private static Context createTestContext() { - return new FakeUiContext(ApplicationProvider.getApplicationContext()); + @Test + public void testGetCurrentWindowLayoutInfo_noFoldingFeature_returnsEmptyList() { + final Context testUiContext = new TestUiContext(mAppContext); + + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); + + assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); + } + + @Test + public void testGetCurrentWindowLayoutInfo_hasFoldingFeature_returnsWindowLayoutInfo() { + final Context testUiContext = new TestUiContext(mAppContext); + final WindowConfiguration windowConfiguration = + testUiContext.getResources().getConfiguration().windowConfiguration; + final Rect featureRect = windowConfiguration.getBounds(); + final CommonFoldingFeature foldingFeature = new CommonFoldingFeature( + CommonFoldingFeature.COMMON_TYPE_HINGE, + CommonFoldingFeature.COMMON_STATE_FLAT, + featureRect + ); + mWindowLayoutComponent.onDisplayFeaturesChanged(List.of(foldingFeature)); + + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(testUiContext); + + assertThat(layoutInfo.getDisplayFeatures()).containsExactly(new FoldingFeature( + featureRect, FoldingFeature.TYPE_HINGE, FoldingFeature.STATE_FLAT)); + } + + @Test + public void testGetCurrentWindowLayoutInfo_nonUiContext_returnsEmptyList() { + final WindowLayoutInfo layoutInfo = + mWindowLayoutComponent.getCurrentWindowLayoutInfo(mAppContext); + + assertThat(layoutInfo.getDisplayFeatures()).isEmpty(); + } + + /** + * A {@link Context} that simulates a UI context specifically for testing purposes. + * This class overrides {@link Context#getAssociatedDisplayId()} to return + * {@link Display#DEFAULT_DISPLAY}, ensuring the context is tied to the default display, + * and {@link Context#isUiContext()} to always return {@code true}, simulating a UI context. + */ + private static class TestUiContext extends ContextWrapper { + + TestUiContext(Context base) { + super(base); + } + + @Override + public int getAssociatedDisplayId() { + return Display.DEFAULT_DISPLAY; + } + + @Override + public boolean isUiContext() { + return true; + } } /** - * A {@link android.content.Context} overrides {@link android.content.Context#isUiContext} to - * {@code true}. + * A {@link Context} that cheats by overriding {@link Context#isUiContext} to always + * return {@code true}. This is useful for scenarios where a UI context is needed, + * but the underlying context isn't actually a UI one. */ private static class FakeUiContext extends ContextWrapper { diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 4642fe59bcb2..42188dec4236 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -57,7 +57,7 @@ filegroup { path: "src", } -genrule { +java_genrule { name: "wm_shell_protolog_src", srcs: [ ":protolog-impl", @@ -77,7 +77,7 @@ genrule { out: ["wm_shell_protolog.srcjar"], } -genrule { +java_genrule { name: "generate-wm_shell_protolog.json", srcs: [ ":wm_shell_protolog-groups", @@ -94,7 +94,7 @@ genrule { out: ["wm_shell_protolog.json"], } -genrule { +java_genrule { name: "gen-wmshell.protolog.pb", srcs: [ ":wm_shell_protolog-groups", @@ -111,7 +111,7 @@ genrule { out: ["wmshell.protolog.pb"], } -genrule { +java_genrule { name: "protolog.json.gz", srcs: [":generate-wm_shell_protolog.json"], out: ["wmshell.protolog.json.gz"], @@ -157,6 +157,13 @@ java_library { } filegroup { + name: "wm_shell-shared-utils", + srcs: [ + "shared/src/com/android/wm/shell/shared/TransitionUtil.java", + ], +} + +filegroup { name: "wm_shell-shared-aidls", srcs: [ diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index cf0a975b6c30..714d5e0de367 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -168,3 +168,13 @@ flag { description: "Enables flexibile split feature for split screen" bug: "349828130" } + +flag { + name: "enable_task_view_controller_cleanup" + namespace: "multitasking" + description: "Fix memory leak with task view controllers" + bug: "369995920" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png Binary files differindex c7b4c65b8c4b..736bca7f5a7c 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png Binary files differindex c7b4c65b8c4b..736bca7f5a7c 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png Binary files differindex c72944222e66..e540b455028b 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png Binary files differindex c72944222e66..e540b455028b 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml index 806d026a7e7c..4a42616a45ec 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml @@ -16,40 +16,42 @@ --> <com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" android:layout_marginTop="@dimen/bubble_popup_margin_top" - android:elevation="@dimen/bubble_manage_menu_elevation" + android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" + android:layout_marginBottom="@dimen/bubble_popup_margin_bottom" + android:elevation="@dimen/bubble_popup_elevation" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView - android:layout_width="32dp" - android:layout_height="32dp" - android:tint="?android:attr/colorAccent" + android:layout_width="@dimen/bubble_popup_icon_size" + android:layout_height="@dimen/bubble_popup_icon_size" + android:tint="?androidprv:attr/materialColorPrimary" android:contentDescription="@null" android:src="@drawable/pip_ic_settings"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:maxLines="1" android:ellipsize="end" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/materialColorOnSurface" android:text="@string/bubble_bar_education_manage_title"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:textAppearance="@android:style/TextAppearance.DeviceDefault" - android:textColor="?android:attr/textColorSecondary" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" android:textAlignment="center" android:text="@string/bubble_bar_education_manage_text"/> diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml index 7fa586c626be..f19c3c762d9d 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml @@ -16,40 +16,42 @@ --> <com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" - android:layout_margin="@dimen/bubble_popup_margin_horizontal" - android:layout_marginBottom="120dp" - android:elevation="@dimen/bubble_manage_menu_elevation" + android:layout_marginTop="@dimen/bubble_popup_margin_top" + android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal" + android:layout_marginBottom="@dimen/bubble_popup_margin_bottom" + android:elevation="@dimen/bubble_popup_elevation" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView - android:layout_width="32dp" - android:layout_height="32dp" - android:tint="?android:attr/colorAccent" + android:layout_width="@dimen/bubble_popup_icon_size" + android:layout_height="@dimen/bubble_popup_icon_size" + android:tint="?androidprv:attr/materialColorOutline" android:contentDescription="@null" android:src="@drawable/ic_floating_landscape"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:maxLines="1" android:ellipsize="end" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/materialColorOnSurface" android:text="@string/bubble_bar_education_stack_title"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/bubble_popup_text_margin" android:maxWidth="@dimen/bubble_popup_content_max_width" android:textAppearance="@android:style/TextAppearance.DeviceDefault" - android:textColor="?android:attr/textColorSecondary" + android:textColor="?androidprv:attr/materialColorOnSurfaceVariant" android:textAlignment="center" android:text="@string/bubble_bar_education_stack_text"/> diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index a0718d9ba148..b29e8bf5e922 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Stel terug"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Spring na regs"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Maak By Verstek Oop-instellings"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index f160c70b7505..d96033f286f8 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ወደነበረበት መልስ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ወደ ቀኝ አሳድግ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"በነባሪ ቅንብሮች ክፈት"</string> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 81706a21f77f..42ef4c307b1e 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"استعادة"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"المحاذاة إلى اليمين"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"إعدادات الفتح تلقائيًا"</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 9fd4941afe84..8e9c7045118f 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্টো আনিব নোৱাৰি"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"সোঁফাললৈ স্নেপ কৰক"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 3ab639730a90..1310f6f743f4 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Bərpa edin"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tərəf çəkin"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Defolt ayarlarla açın"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 507625661527..b9c42397f8c7 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vratite"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prikačite desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Podešavanje Podrazumevano otvaraj"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 95530c44562e..5d389d849433 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Аднавіць"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Размясціць справа"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налады параметра \"Адкрываць стандартна\""</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index db498c1dfe1a..807878620d90 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Възстановяване"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прилепване надясно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отваряне на настройките по подразбиране"</string> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 4c2025378f04..8db7144e6db6 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ডানদিকে স্ন্যাপ করুন"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 102a91233627..3d922d8334c8 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vraćanje"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pomicanje udesno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvaranje prema zadanim postavkama"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 3e3fcd05a05b..dc96cd0f72ec 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaura"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajusta a la dreta"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configuració d\'obertura predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 0627f54ecf47..30ba78ad8e06 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovit"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Přichytit vpravo"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otevírat podle výchozího nastavení"</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index ed9e5f07bad3..433b858cfd84 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gendan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fastgør til højre"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Indstillinger for automatisk åbning"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index ec1cb03d7108..0b4d189716c2 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximieren"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links andocken"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts andocken"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Einstellungen für die Option „Standardmäßig öffnen“"</string> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 7a690ce2e188..beff019e6897 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Επαναφορά"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Κούμπωμα δεξιά"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Άνοιγμα ρυθμίσεων από προεπιλογή"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index aa392519145b..20ec076a33d1 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index afb3f8e1a5ce..01d3d25e7e4f 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 5244a61883d0..c9e7ecbb056e 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restablecer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar a la derecha"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con la configuración predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index a673351f8316..903294bbd1cf 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Acoplar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Acoplar a la derecha"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con los ajustes predeterminados"</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 686385c4bdb8..f6bebff9a4f3 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Taasta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Tõmmake paremale"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avamisviisi vaikeseaded"</string> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 1f0e54cedd58..267452f2e6e1 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Leheneratu"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ezarri eskuinean"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Modu lehenetsian irekitzearen ezarpenak"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index ad39b28eed1e..ec4779b0a129 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمیتوان به اینجا منتقل کرد"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بازیابی کردن"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن بهچپ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"کشیدن بهراست"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"تنظیمات باز کردن بهطور پیشفرض"</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 2f0edd33226b..6dcd20cc0227 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Palauta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Siirrä oikealle"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avaa oletusasetusten mukaan"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 245396156aad..df6d503c2121 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Épingler à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Épingler à droite"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index aee234565ad0..3a7f2f0bd4fa 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Agrandir"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ancrer à gauche"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ancrer à droite"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ouvrir les paramètres par défaut"</string> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index b61588ac24ed..cb7bb324de27 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Axustar á dereita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir coa configuración predeterminada"</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index fd4f2baeb871..bee06fed5fd1 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"જમણે સ્નૅપ કરો"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index d2cd23df8296..2c141996b109 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"पहले जैसा करें"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दाईं ओर स्नैप करें"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफ़ॉल्ट सेटिंग के हिसाब से खोलें"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 80949b4c8edd..a2a52d13b463 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Vrati"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Poravnaj desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvori prema zadanim postavkama"</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index cebf5857db34..03cc9f569d29 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Visszaállítás"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Jobbra igazítás"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Alapértelmezett beállítások megnyitása"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 63a9d6d92134..28c762eb0752 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Վերականգնել"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ամրացնել աջ կողմում"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Բացել կարգավորումներն ըստ կանխադրման"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index a06d01cc524a..4929b79875a0 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Maksimalkan ke kanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka dengan setelan default"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index a20f4604ff90..d7ba53f73926 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Endurheimta"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Smella til hægri"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Stillingar sjálfvirkrar opnunar"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 39fd6ba326a6..4522d37d289a 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ingrandisci"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Ripristina"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Aggancia a sinistra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Aggancia a destra"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Apri in base alle impostazioni predefinite"</string> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index 0586d1f8cd16..110d357d0cb2 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"שחזור"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"הצמדה לימין"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 8aa8269af8a5..69421da5b450 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"復元"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"右にスナップ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"デフォルトの設定で開く"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index 6672599d2763..46859889304a 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"აღდგენა"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"მარჯვნივ გადატანა"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"პარამეტრების ნაგულისხმევად გახსნა"</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 56ae4416192a..84e7ea5b7af3 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Қалпына келтіру"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңға тіркеу"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Әдепкісінше ашу параметрлері"</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 460b8678c2c9..7eb81e158513 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ស្ដារ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ផ្លាស់ទីទៅស្ដាំ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ការកំណត់បើកតាមលំនាំដើម"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 2e2be46c21f6..2b43f573e1ab 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ಮರುಸ್ಥಾಪಿಸಿ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ಬಲಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ಡೀಫಾಲ್ಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ತೆರೆಯಿರಿ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 4bcc76dd0259..8f36aab7ce8a 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"복원"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"오른쪽으로 맞추기"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"기본값으로 열기 설정"</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 6ae51a4ab770..c1219ba86249 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Калыбына келтирүү"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңго жылдыруу"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачуу параметрлери"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index f8a09da308b9..375fc6435935 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ແນບຂວາ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 857e90e6d340..dfc3b45786de 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atkurti"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pritraukti dešinėje"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atidaryti pagal numatytuosius nustatymus"</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index e56363e06c46..87818524cabe 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Atjaunot"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Piestiprināt pa labi"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atvērt pēc noklusējuma iestatījumiem"</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 1bf7a28aa410..88fed7414758 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Максимизирај"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Врати"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Фотографирај лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Фотографирај десно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отвори според стандардните поставки"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 3401f6d6b54a..71fb78eca0f3 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്ക്രീൻ സ്നാപ്പ് ചെയ്യുക"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"പുനഃസ്ഥാപിക്കുക"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"വലതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ഡിഫോൾട്ട് ക്രമീകരണം ഉപയോഗിച്ച് തുറക്കുക"</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 87708d0a0cc2..04f2f8202961 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Баруун тийш зэрэгцүүлэх"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index 1ea41e557c4f..be1df3273b21 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अॅप इथे हलवू शकत नाही"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोअर करा"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"उजवीकडे स्नॅप करा"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"बाय डीफॉल्ट सेटिंग्ज उघडा"</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index ca248e133eb8..04da8869d5a7 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Pulihkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Autojajar ke kanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka tetapan secara lalai"</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 3c4325bfc5b8..915a7cd3d67a 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"ပြန်ပြောင်းရန်"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ညာတွင် ချဲ့ရန်"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"မူရင်းဆက်တင်ဖြင့် ဖွင့်ရန်"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 4096bbf69fe7..8e5aee1eb49b 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Gjenopprett"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fest til høyre"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Innstillinger for åpning som standard"</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 2fc5e0902efa..42f2336c63ff 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"रिस्टोर गर्नुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दायाँतिर स्न्याप गर्नुहोस्"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफल्ट सेटिङअनुसार खोल्नुहोस्"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 65fd8ea44d2e..d19a4d44d6f9 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Herstellen"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts uitlijnen"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Instellingen voor Standaard openen"</string> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 1f96daad93b9..f67a6b2b7d38 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ବଡ଼ କରନ୍ତୁ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ବାମରେ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ଡାହାଣରେ ସ୍ନାପ କରନ୍ତୁ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ଡିଫଲ୍ଟ ସେଟିଂସକୁ ଖୋଲନ୍ତୁ"</string> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index f93f5097ac66..76d59af75bfb 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ਸੱਜੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੈਟਿੰਗਾਂ ਮੁਤਾਬਕ ਖੋਲ੍ਹੋ"</string> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 1e76e82094d8..502e53abdcd3 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Przywróć"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Przyciągnij do prawej"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ustawienia domyślnego otwierania"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 0bdb0786ef71..3ec5e76bc5b4 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 0e10da1e6e0a..184a5b336fd8 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Encaixar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Definições de Abrir por predefinição"</string> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 0bdb0786ef71..3ec5e76bc5b4 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restaurar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index c94273630432..9703328d4d98 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Restabilește"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Trage la dreapta"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Setări de deschidere în mod prestabilit"</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index e2c39387c8b4..401a8aab2576 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Восстановить"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Привязать справа"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Настройки, регулирующие, как по умолчанию открываются ссылки"</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 83a09f5beffe..c101b4cd757c 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"දකුණට ස්නැප් කරන්න"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 1b3907e5775b..7214300f1eb7 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnoviť"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prichytiť vpravo"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvárať podľa predvolených nastavení"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 0a1b4a691313..04fe7e89c16e 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Obnovi"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pripni desno"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Nastavitve privzetega odpiranja"</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 75120d2418b5..6662ca1a059d 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -134,6 +134,8 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizo"</string> + <!-- no translation found for desktop_mode_maximize_menu_restore_button_text (4234449220944704387) --> + <skip /> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Zhvendos majtas"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Zhvendos djathtas"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Hap sipas cilësimeve të parazgjedhura"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 8b5c4dfff363..d0a4ae684af7 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Вратите"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прикачите десно"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Подешавање Подразумевано отварај"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index e40b6492fc71..6ce2a9a1cb4d 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Återställ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fäst till höger"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för Öppna som standard"</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index e63229ccf2cd..40967f02be45 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Rejesha"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Telezesha kulia"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Fungua kwa mipangilio chaguomsingi"</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 95972f1f9486..3140c2c77fae 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"மீட்டெடுக்கும்"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"வலதுபுறம் நகர்த்தும்"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"இயல்பாக அமைப்புகளைத் திறக்கும்"</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 6223c83d7599..62e62c7a1e25 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్ను స్నాప్ చేయండి"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"రీస్టోర్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"కుడి వైపున స్నాప్ చేయండి"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"ఆటోమేటిక్ సెట్టింగ్ల ద్వారా తెరవండి"</string> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index f74499c0ddbf..e6386a20e492 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"คืนค่า"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"จัดพอดีกับทางขวา"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"เปิดตามการตั้งค่าเริ่มต้น"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index 7d984e0a1c14..176be336117d 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"I-restore"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"I-snap pakanan"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buksan sa pamamagitan ng mga default na setting"</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index ba186aae80c8..73248e3d0c96 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Geri yükle"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tuttur"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Varsayılan olarak açma ayarları"</string> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 756e64da4574..a655a3eb452c 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Розгорнути"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Відновити"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Закріпити ліворуч"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Закріпити праворуч"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налаштування \"Відкривати за умовчанням\""</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 8aaa306a2ada..4bdea83e59e3 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"بحال کریں"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"بائیں منتقل کریں"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"بطور ڈیفالٹ ترتیبات کھولیں"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 4e4a58ba25dc..b3a496f27582 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Yoyish"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Tiklash"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chapga tortish"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Oʻngga tortish"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Birlamchi sozlamalar asosida ochish"</string> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 09a143af61a0..36d66e432590 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Khôi phục"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Di chuyển nhanh sang phải"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Mở các chế độ cài đặt theo mặc định"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 795febb0ee3f..64446cd4b342 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"恢复"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"贴靠右侧"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"默认打开设置"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 0c6ad618ec58..4970e8b92afb 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"貼齊右邊"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"採用預設設定打開"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 442a6feae787..fcdcccaee5f5 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"還原"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"靠右對齊"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"開啟連結的預設設定"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 47613d451a33..cbc6c022a3c6 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -134,6 +134,7 @@ <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string> <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string> + <string name="desktop_mode_maximize_menu_restore_button_text" msgid="4234449220944704387">"Buyisela"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Chofoza kwesokudla"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"Vula amasethingi ngokuzenzakalela"</string> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index a14461a57a95..86751d45f378 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -45,6 +45,9 @@ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu --> <bool name="config_pipEnableResizeForMenu">true</bool> + <!-- Allow PIP to resize via dragging the corner of PiP. --> + <bool name="config_pipEnableDragCornerResize">false</bool> + <!-- Time (duration in milliseconds) that the shell waits for an app to close the PiP by itself if a custom action is present before closing it. --> <integer name="config_pipForceCloseDelay">1000</integer> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index df1e2248872b..d02c77e831aa 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -240,8 +240,14 @@ <dimen name="bubble_popup_content_max_width">300dp</dimen> <!-- Horizontal margin for the bubble popup view. --> <dimen name="bubble_popup_margin_horizontal">32dp</dimen> - <!-- Top margin for the bubble popup view. --> - <dimen name="bubble_popup_margin_top">16dp</dimen> + <!-- Top margin for the bubble bar education views. --> + <dimen name="bubble_popup_margin_top">24dp</dimen> + <!-- Bottom margin for the bubble bar education views. --> + <dimen name="bubble_popup_margin_bottom">32dp</dimen> + <!-- Text margin for the bubble bar education views. --> + <dimen name="bubble_popup_text_margin">16dp</dimen> + <!-- Size of icons in the bubble bar education views. --> + <dimen name="bubble_popup_icon_size">32dp</dimen> <!-- Width for the bubble popup view arrow. --> <dimen name="bubble_popup_arrow_width">12dp</dimen> <!-- Height for the bubble popup view arrow. --> @@ -250,6 +256,8 @@ <dimen name="bubble_popup_arrow_corner_radius">2dp</dimen> <!-- Padding for the bubble popup view contents. --> <dimen name="bubble_popup_padding">24dp</dimen> + <!-- Elevation of the popup user education views for the bubble bar --> + <dimen name="bubble_popup_elevation">2dp</dimen> <!-- The size of the caption bar inset at the top of bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_caption_height">36dp</dimen> <!-- The width of the caption bar at the top of bubble bar expanded view. --> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt index 79becb0a2e20..0e8e90467745 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt @@ -122,7 +122,8 @@ abstract class ManageWindowsViewContainer( snapshot.hardwareBuffer, snapshot.colorSpace ) - val scaledSnapshotBitmap = snapshotBitmap?.let { + val croppedBitmap = snapshotBitmap?.let { cropBitmap(it) } + val scaledSnapshotBitmap = croppedBitmap?.let { Bitmap.createScaledBitmap( it, instanceIconWidth.toInt(), instanceIconHeight.toInt(), true /* filter */ ) @@ -160,6 +161,35 @@ abstract class ManageWindowsViewContainer( menuHeight += iconMargin.toInt() } + private fun cropBitmap( + bitmapToCrop: Bitmap + ): Bitmap { + val ratioToMatch = ICON_WIDTH_DP / ICON_HEIGHT_DP + val bitmapWidth = bitmapToCrop.width + val bitmapHeight = bitmapToCrop.height + if (bitmapWidth > bitmapHeight * ratioToMatch) { + // Crop based on height + val newWidth = bitmapHeight * ratioToMatch + return Bitmap.createBitmap( + bitmapToCrop, + ((bitmapWidth - newWidth) / 2).toInt(), + 0, + newWidth.toInt(), + bitmapHeight + ) + } else { + // Crop based on width + val newHeight = bitmapWidth / ratioToMatch + return Bitmap.createBitmap( + bitmapToCrop, + 0, + ((bitmapHeight - newHeight) / 2).toInt(), + bitmapWidth, + newHeight.toInt() + ) + } + } + companion object { private const val MENU_RADIUS_DP = 26f private const val ICON_WIDTH_DP = 204f diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index e4db7b636ed9..5eb5d8962b55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -95,6 +95,7 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -120,7 +121,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @ShellMainThread private final Handler mHandler; /** True when a back gesture is ongoing */ - private boolean mBackGestureStarted = false; + @VisibleForTesting public boolean mBackGestureStarted = false; /** Tracks if an uninterruptible animation is in progress */ private boolean mPostCommitAnimationInProgress = false; @@ -511,6 +512,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Ignoring MotionEvent because two gestures are already being queued."); return; + } else if (mBackGestureStarted && mCurrentTracker.isInInitialState() + && mQueuedTracker.isInInitialState()) { + ProtoLog.e(WM_SHELL_BACK_PREVIEW, + "Both touch trackers in initial state and mBackGestureStarted=true"); + mBackGestureStarted = false; } if (keyAction == MotionEvent.ACTION_DOWN) { @@ -985,7 +991,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); mActiveCallback = null; mApps = null; - mShouldStartOnNextMoveEvent = false; mOnBackStartDispatched = false; mThresholdCrossed = false; mPointersPilfered = false; @@ -1273,42 +1278,42 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } // Copy initial changes to final transition final TransitionInfo init = mOpenTransitionInfo; - // find prepare open target + // Find prepare open target boolean openShowWallpaper = false; - ComponentName openComponent = null; + final ArrayList<OpenChangeInfo> targets = new ArrayList<>(); int tmpSize; - int openTaskId = INVALID_TASK_ID; - WindowContainerToken openToken = null; for (int j = init.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = init.getChanges().get(j); - if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { - openComponent = findComponentName(change); - openTaskId = findTaskId(change); - openToken = findToken(change); + if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED) + && TransitionUtil.isOpeningMode(change.getMode())) { + final ComponentName openComponent = findComponentName(change); + final int openTaskId = findTaskId(change); + final WindowContainerToken openToken = findToken(change); + if (openComponent == null && openTaskId == INVALID_TASK_ID + && openToken == null) { + continue; + } + targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken)); if (change.hasFlags(FLAG_SHOW_WALLPAPER)) { openShowWallpaper = true; } - break; } } - if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) { + if (targets.isEmpty()) { // This shouldn't happen, but if that happen, consume the initial transition anyway. Log.e(TAG, "Unable to merge following transition, cannot find the gesture " + "animated target from the open transition=" + mOpenTransitionInfo); mOpenTransitionInfo = null; return; } - // find first non-prepare open target + // Find first non-prepare open target boolean isOpen = false; tmpSize = info.getChanges().size(); for (int j = 0; j < tmpSize; ++j) { final TransitionInfo.Change change = info.getChanges().get(j); - final ComponentName firstNonOpen = findComponentName(change); - final int firstTaskId = findTaskId(change); - if ((firstNonOpen != null && firstNonOpen != openComponent) - || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) { - // this is original close target, potential be close, but cannot determine from - // it + if (isOpenChangeMatched(targets, change)) { + // This is original close target, potential be close, but cannot determine + // from it. if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { isOpen = !TransitionUtil.isClosingMode(change.getMode()); } else { @@ -1317,33 +1322,44 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } } - if (!isOpen) { // Close transition, the transition info should be: // init info(open A & wallpaper) // current info(close B target) // remove init info(open/change A target & wallpaper) boolean moveToTop = false; + boolean excludeOpenTarget = false; + boolean mergePredictive = false; for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { + if (TransitionUtil.isClosingMode(change.getMode())) { + excludeOpenTarget = true; + } moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP); info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER)) || !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { info.getChanges().remove(j); + } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) { + mergePredictive = true; } } // Ignore merge if there is no close target - if (!info.getChanges().isEmpty()) { + if (!info.getChanges().isEmpty() && mergePredictive) { tmpSize = init.getChanges().size(); for (int i = 0; i < tmpSize; ++i) { final TransitionInfo.Change change = init.getChanges().get(i); if (change.hasFlags(FLAG_IS_WALLPAPER)) { continue; } - if (moveToTop) { - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { + if (excludeOpenTarget) { + // App has triggered another change during predictive back + // transition, filter out predictive back target. + continue; + } + if (moveToTop) { change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP); } } @@ -1372,7 +1388,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (nonBackClose && nonBackOpen) { for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { + if (isOpenChangeMatched(targets, change)) { info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) { info.getChanges().remove(j); @@ -1655,9 +1671,21 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont final ComponentName openChange = findComponentName(change); final int firstTaskId = findTaskId(change); final WindowContainerToken openToken = findToken(change); - return (openChange != null && openChange == topActivity) + return (openChange != null && openChange.equals(topActivity)) || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId) - || (openToken != null && token == openToken); + || (openToken != null && openToken.equals(token)); + } + + static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets, + TransitionInfo.Change change) { + for (int i = targets.size() - 1; i >= 0; --i) { + final OpenChangeInfo next = targets.get(i); + if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken, + change)) { + return true; + } + } + return false; } private static boolean canBeTransitionTarget(TransitionInfo.Change change) { @@ -1717,4 +1745,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } } + + static class OpenChangeInfo { + final ComponentName mOpenComponent; + final int mOpenTaskId; + final WindowContainerToken mOpenToken; + OpenChangeInfo(ComponentName openComponent, int openTaskId, + WindowContainerToken openToken) { + mOpenComponent = openComponent; + mOpenTaskId = openTaskId; + mOpenToken = openToken; + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index 7a569799ab84..dc50fdbd1af3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -16,12 +16,15 @@ package com.android.wm.shell.back; +import static android.view.MotionEvent.ACTION_MOVE; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.window.BackEvent.EDGE_RIGHT; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD; +import static com.android.wm.shell.back.CrossActivityBackAnimationKt.scaleCentered; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; import android.animation.Animator; @@ -36,11 +39,14 @@ import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.os.RemoteException; +import android.util.TimeUtils; import android.view.Choreographer; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; +import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.VelocityTracker; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.window.BackEvent; @@ -48,6 +54,9 @@ import android.window.BackMotionEvent; import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; +import com.android.internal.dynamicanimation.animation.FloatValueHolder; +import com.android.internal.dynamicanimation.animation.SpringAnimation; +import com.android.internal.dynamicanimation.animation.SpringForce; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; import com.android.internal.protolog.ProtoLog; @@ -81,6 +90,11 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { /** Duration of post animation after gesture committed. */ private static final int POST_ANIMATION_DURATION_MS = 500; + private static final float SPRING_SCALE = 100f; + private static final float DEFAULT_FLING_VELOCITY = 320f; + private static final float MAX_FLING_VELOCITY = 1000f; + private static final float FLING_SPRING_STIFFNESS = 320f; + private final Rect mStartTaskRect = new Rect(); private float mCornerRadius; private int mStatusbarHeight; @@ -114,6 +128,14 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private float mInterWindowMargin; private float mVerticalMargin; + private final FloatValueHolder mPostCommitFlingScale = new FloatValueHolder(SPRING_SCALE); + private final SpringForce mPostCommitFlingSpring = new SpringForce(SPRING_SCALE) + .setStiffness(FLING_SPRING_STIFFNESS) + .setDampingRatio(1f); + private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private float mGestureProgress = 0f; + private long mDownTime = 0L; + @Inject public CrossTaskBackAnimation(Context context, BackAnimationBackground background, @ShellMainThread Handler handler) { @@ -168,6 +190,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { if (mEnteringTarget == null || mClosingTarget == null) { return; } + mGestureProgress = progress; float touchY = event.getTouchY(); @@ -229,6 +252,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { } mClosingCurrentRect.set(left, top, left + width, top + height); + + applyFlingScale(mClosingCurrentRect); applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius); } @@ -239,9 +264,19 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height()); mEnteringCurrentRect.set(left, top, left + width, top + height); + + applyFlingScale(mEnteringCurrentRect); applyTransform(mEnteringTarget.leash, mEnteringCurrentRect, mCornerRadius); } + private void applyFlingScale(RectF rect) { + // apply a scale to the rect to account for fling velocity + final float flingScale = Math.min(mPostCommitFlingScale.getValue() / SPRING_SCALE, 1f); + if (flingScale >= 1f) return; + scaleCentered(rect, flingScale, /* pivotX */ rect.right, + /* pivotY */ rect.top + rect.height() / 2); + } + /** Transform the target window to match the target rect. */ private void applyTransform(SurfaceControl leash, RectF targetRect, float cornerRadius) { if (leash == null || !leash.isValid()) { @@ -280,6 +315,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mTransformMatrix.reset(); mClosingCurrentRect.setEmpty(); mInitialTouchPos.set(0, 0); + mGestureProgress = 0; + mDownTime = 0; + mVelocityTracker.clear(); if (mFinishCallback != null) { try { @@ -295,10 +333,24 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private void onGestureProgress(@NonNull BackEvent backEvent) { if (!mBackInProgress) { mBackInProgress = true; + mDownTime = backEvent.getFrameTimeMillis(); } float progress = backEvent.getProgress(); mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); - updateGestureBackProgress(getInterpolatedProgress(progress), backEvent); + float interpolatedProgress = getInterpolatedProgress(progress); + if (predictiveBackTimestampApi()) { + mVelocityTracker.addMovement( + MotionEvent.obtain( + /* downTime */ mDownTime, + /* eventTime */ backEvent.getFrameTimeMillis(), + /* action */ ACTION_MOVE, + /* x */ interpolatedProgress * SPRING_SCALE, + /* y */ 0f, + /* metaState */ 0 + ) + ); + } + updateGestureBackProgress(interpolatedProgress, backEvent); } private void onGestureCommitted() { @@ -307,6 +359,25 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { return; } + if (predictiveBackTimestampApi()) { + // kick off spring animation with the current velocity from the pre-commit phase, this + // affects the scaling of the closing and/or opening task during post-commit + mVelocityTracker.computeCurrentVelocity(1000); + float startVelocity = mGestureProgress < 0.1f + ? -DEFAULT_FLING_VELOCITY : -mVelocityTracker.getXVelocity(); + SpringAnimation flingAnimation = + new SpringAnimation(mPostCommitFlingScale, SPRING_SCALE) + .setStartVelocity(Math.max(-MAX_FLING_VELOCITY, Math.min(0f, startVelocity))) + .setStartValue(SPRING_SCALE) + .setMinimumVisibleChange(0.1f) + .setSpring(mPostCommitFlingSpring); + flingAnimation.start(); + // do an animation-frame immediately to prevent idle frame + flingAnimation.doAnimationFrame( + Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS + ); + } + // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current // coordinate of the gesture driven phase. mEnteringCurrentRect.round(mEnteringStartRect); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt index fd110a276826..9b3054e9ee13 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt @@ -27,7 +27,7 @@ fun BubblePopupView.setup() { val attrs = context.obtainStyledAttributes( intArrayOf( - com.android.internal.R.attr.materialColorSurface, + com.android.internal.R.attr.materialColorSurfaceContainer, android.R.attr.dialogCornerRadius ) ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt index 9fd255ded0ad..7adec39c9d66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt @@ -20,6 +20,7 @@ import android.content.Context import android.graphics.Point import android.graphics.Rect import android.util.Log +import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -102,14 +103,17 @@ class BubbleEducationViewController(private val context: Context, private val li hideEducation(animated = false) log { "showStackEducation at: $position" } + val rootBounds = Rect() + // Get root bounds on screen as position is in screen coordinates + root.getBoundsOnScreen(rootBounds) educationView = createEducationView(R.layout.bubble_bar_stack_education, root).apply { setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN) - setArrowPosition(BubblePopupDrawable.ArrowPosition.End) - updateEducationPosition(view = this, position, root) + updateEducationPosition(view = this, position, rootBounds) val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f doOnLayout { - it.pivotX = it.width - arrowToEdgeOffset + it.pivotX = if (position.x < rootBounds.centerX()) + arrowToEdgeOffset else it.width - arrowToEdgeOffset it.pivotY = it.height.toFloat() } setOnClickListener { educationClickHandler() } @@ -218,12 +222,9 @@ class BubbleEducationViewController(private val context: Context, private val li * * @param view the user education view to layout * @param position the reference position in Screen coordinates - * @param root the root view to use for the layout + * @param rootBounds bounds of the parent the education view is placed in */ - private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) { - val rootBounds = Rect() - // Get root bounds on screen as position is in screen coordinates - root.getBoundsOnScreen(rootBounds) + private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) { // Get the offset to the arrow from the edge of the education view val arrowToEdgeOffset = view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt() @@ -231,7 +232,15 @@ class BubbleEducationViewController(private val context: Context, private val li // Calculate education view margins val params = view.layoutParams as FrameLayout.LayoutParams params.bottomMargin = rootBounds.bottom - position.y - params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset + if (position.x < rootBounds.centerX()) { + params.leftMargin = position.x - arrowToEdgeOffset + params.gravity = Gravity.LEFT or Gravity.BOTTOM + view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start) + } else { + params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset + params.gravity = Gravity.RIGHT or Gravity.BOTTOM + view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End) + } view.layoutParams = params } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java index 915a8a149d54..37369d1f3047 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIStatusManager.java @@ -24,6 +24,7 @@ import java.util.function.IntSupplier; /** Handle the visibility state of the Compat UI components. */ public class CompatUIStatusManager { + private static final int COMPAT_UI_EDUCATION_UNDEFINED = -1; public static final int COMPAT_UI_EDUCATION_HIDDEN = 0; public static final int COMPAT_UI_EDUCATION_VISIBLE = 1; @@ -32,24 +33,40 @@ public class CompatUIStatusManager { @NonNull private final IntSupplier mReader; + private int mCurrentValue = COMPAT_UI_EDUCATION_UNDEFINED; + public CompatUIStatusManager(@NonNull IntConsumer writer, @NonNull IntSupplier reader) { mWriter = writer; mReader = reader; } public CompatUIStatusManager() { - this(i -> { }, () -> COMPAT_UI_EDUCATION_HIDDEN); + this(i -> { + }, () -> COMPAT_UI_EDUCATION_HIDDEN); } void onEducationShown() { - mWriter.accept(COMPAT_UI_EDUCATION_VISIBLE); + if (mCurrentValue != COMPAT_UI_EDUCATION_VISIBLE) { + mCurrentValue = COMPAT_UI_EDUCATION_VISIBLE; + mWriter.accept(mCurrentValue); + } } void onEducationHidden() { - mWriter.accept(COMPAT_UI_EDUCATION_HIDDEN); + if (mCurrentValue != COMPAT_UI_EDUCATION_HIDDEN) { + mCurrentValue = COMPAT_UI_EDUCATION_HIDDEN; + mWriter.accept(mCurrentValue); + } } boolean isEducationVisible() { - return mReader.getAsInt() == COMPAT_UI_EDUCATION_VISIBLE; + return getCurrentValue() == COMPAT_UI_EDUCATION_VISIBLE; + } + + private int getCurrentValue() { + if (mCurrentValue == COMPAT_UI_EDUCATION_UNDEFINED) { + mCurrentValue = mReader.getAsInt(); + } + return mCurrentValue; } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 427df1784506..7f547868b3e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -17,7 +17,6 @@ package com.android.wm.shell.dagger; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS; -import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS; import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT; import android.annotation.Nullable; @@ -67,7 +66,7 @@ import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; @@ -267,7 +266,8 @@ public abstract class WMShellModule { AppHandleEducationController appHandleEducationController, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -295,7 +295,8 @@ public abstract class WMShellModule { appHandleEducationController, windowDecorCaptionHandleRepository, desktopActivityOrientationHandler, - focusTransitionObserver); + focusTransitionObserver, + desktopModeEventLogger); } return new CaptionWindowDecorViewModel( context, @@ -395,12 +396,12 @@ public abstract class WMShellModule { Context context, ShellInit shellInit, Transitions transitions, - Optional<DesktopFullImmersiveTransitionHandler> desktopImmersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, WindowDecorViewModel windowDecorViewModel, Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver) { return new FreeformTaskTransitionObserver( - context, shellInit, transitions, desktopImmersiveTransitionHandler, + context, shellInit, transitions, desktopImmersiveController, windowDecorViewModel, taskChangeListener, focusTransitionObserver); } @@ -636,7 +637,7 @@ public abstract class WMShellModule { ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, DragToDesktopTransitionHandler dragToDesktopTransitionHandler, @DynamicOverride DesktopRepository desktopRepository, - Optional<DesktopFullImmersiveTransitionHandler> desktopFullImmersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver, LaunchAdjacentController launchAdjacentController, RecentsTransitionHandler recentsTransitionHandler, @@ -647,19 +648,21 @@ public abstract class WMShellModule { Optional<RecentTasksController> recentTasksController, InteractionJankMonitor interactionJankMonitor, InputManager inputManager, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, dragAndDropController, transitions, keyguardManager, returnToDragStartAnimator, enterDesktopTransitionHandler, exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler, toggleResizeDesktopTaskTransitionHandler, - dragToDesktopTransitionHandler, desktopFullImmersiveTransitionHandler.get(), + dragToDesktopTransitionHandler, desktopImmersiveController.get(), desktopRepository, desktopModeLoggerTransitionObserver, launchAdjacentController, recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null), interactionJankMonitor, mainHandler, - inputManager, focusTransitionObserver); + inputManager, focusTransitionObserver, + desktopModeEventLogger); } @WMSingleton @@ -701,7 +704,7 @@ public abstract class WMShellModule { @WMSingleton @Provides - static Optional<DesktopFullImmersiveTransitionHandler> provideDesktopImmersiveHandler( + static Optional<DesktopImmersiveController> provideDesktopImmersiveController( Context context, Transitions transitions, @DynamicOverride DesktopRepository desktopRepository, @@ -709,7 +712,7 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.of( - new DesktopFullImmersiveTransitionHandler( + new DesktopImmersiveController( transitions, desktopRepository, displayController, @@ -844,8 +847,7 @@ public abstract class WMShellModule { InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler ) { - if (!DesktopModeStatus.canEnterDesktopMode(context) - || !ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue()) { + if (!DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.empty(); } return Optional.of( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt index 9d4926b47def..d0bc5f0955f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt @@ -36,20 +36,21 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionHandler +import com.android.wm.shell.transition.Transitions.TransitionObserver import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener /** - * A [TransitionHandler] to move a task in/out of desktop's full immersive state where the task + * A controller to move tasks in/out of desktop's full immersive state where the task * remains freeform while being able to take fullscreen bounds and have its App Header visibility * be transient below the status bar like in fullscreen immersive mode. */ -class DesktopFullImmersiveTransitionHandler( +class DesktopImmersiveController( private val transitions: Transitions, private val desktopRepository: DesktopRepository, private val displayController: DisplayController, private val shellTaskOrganizer: ShellTaskOrganizer, private val transactionSupplier: () -> SurfaceControl.Transaction, -) : TransitionHandler { +) : TransitionHandler, TransitionObserver { constructor( transitions: Transitions, @@ -67,7 +68,7 @@ class DesktopFullImmersiveTransitionHandler( private var state: TransitionState? = null @VisibleForTesting - val pendingExternalExitTransitions = mutableSetOf<ExternalPendingExit>() + val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>() /** Whether there is an immersive transition that hasn't completed yet. */ private val inProgress: Boolean @@ -137,14 +138,19 @@ class DesktopFullImmersiveTransitionHandler( * * @param wct that will apply these changes * @param displayId of the display that should exit immersive mode + * @param excludeTaskId of the task to ignore (not exit) if it is the immersive one * @return a function to apply once the transition that will apply these changes is started */ fun exitImmersiveIfApplicable( wct: WindowContainerTransaction, - displayId: Int + displayId: Int, + excludeTaskId: Int? = null, ): ((IBinder) -> Unit)? { if (!Flags.enableFullyImmersiveInDesktop()) return null val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null + if (immersiveTask == excludeTaskId) { + return null + } val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null logV("Appending immersive exit for task: $immersiveTask in display: $displayId") wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo)) @@ -179,6 +185,17 @@ class DesktopFullImmersiveTransitionHandler( return null } + + /** Whether the [change] in the [transition] is a known immersive change. */ + fun isImmersiveChange( + transition: IBinder, + change: TransitionInfo.Change, + ): Boolean { + return pendingExternalExitTransitions.any { + it.transition == transition && it.taskId == change.taskInfo?.taskId + } + } + private fun addPendingImmersiveExit(taskId: Int, displayId: Int, transition: IBinder) { pendingExternalExitTransitions.add( ExternalPendingExit( @@ -196,10 +213,11 @@ class DesktopFullImmersiveTransitionHandler( finishTransaction: SurfaceControl.Transaction, finishCallback: Transitions.TransitionFinishCallback ): Boolean { + logD("startAnimation transition=%s", transition) val state = requireState() if (transition != state.transition) return false animateResize( - transitionState = state, + targetTaskId = state.taskId, info = info, startTransaction = startTransaction, finishTransaction = finishTransaction, @@ -209,40 +227,55 @@ class DesktopFullImmersiveTransitionHandler( } private fun animateResize( - transitionState: TransitionState, + targetTaskId: Int, info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, finishCallback: Transitions.TransitionFinishCallback ) { + logD("animateResize for task#%d", targetTaskId) val change = info.changes.first { c -> val taskInfo = c.taskInfo - return@first taskInfo != null && taskInfo.taskId == transitionState.taskId + return@first taskInfo != null && taskInfo.taskId == targetTaskId } + animateResizeChange(change, startTransaction, finishTransaction, finishCallback) + } + + /** + * Animate an immersive change. + * + * As of now, both enter and exit transitions have the same animation, a veiled resize. + */ + fun animateResizeChange( + change: TransitionInfo.Change, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: Transitions.TransitionFinishCallback, + ) { + val taskId = change.taskInfo!!.taskId val leash = change.leash val startBounds = change.startAbsBounds val endBounds = change.endAbsBounds - + logD("Animating resize change for task#%d from %s to %s", taskId, startBounds, endBounds) + + startTransaction + .setPosition(leash, startBounds.left.toFloat(), startBounds.top.toFloat()) + .setWindowCrop(leash, startBounds.width(), startBounds.height()) + .show(leash) + onTaskResizeAnimationListener + ?.onAnimationStart(taskId, startTransaction, startBounds) + ?: startTransaction.apply() val updateTransaction = transactionSupplier() ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds).apply { duration = FULL_IMMERSIVE_ANIM_DURATION_MS interpolator = DecelerateInterpolator() addListener( - onStart = { - startTransaction - .setPosition(leash, startBounds.left.toFloat(), startBounds.top.toFloat()) - .setWindowCrop(leash, startBounds.width(), startBounds.height()) - .show(leash) - onTaskResizeAnimationListener - ?.onAnimationStart(transitionState.taskId, startTransaction, startBounds) - ?: startTransaction.apply() - }, onEnd = { finishTransaction .setPosition(leash, endBounds.left.toFloat(), endBounds.top.toFloat()) .setWindowCrop(leash, endBounds.width(), endBounds.height()) .apply() - onTaskResizeAnimationListener?.onAnimationEnd(transitionState.taskId) + onTaskResizeAnimationListener?.onAnimationEnd(taskId) finishCallback.onTransitionFinished(null /* wct */) clearState() } @@ -254,7 +287,7 @@ class DesktopFullImmersiveTransitionHandler( .setWindowCrop(leash, rect.width(), rect.height()) .apply() onTaskResizeAnimationListener - ?.onBoundsChange(transitionState.taskId, updateTransaction, rect) + ?.onBoundsChange(taskId, updateTransaction, rect) ?: updateTransaction.apply() } start() @@ -284,15 +317,20 @@ class DesktopFullImmersiveTransitionHandler( * |onTransitionReady|, before this transition actually animates) because drawing decorations * depends on whether the task is in full immersive state or not. */ - fun onTransitionReady(transition: IBinder, info: TransitionInfo) { + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + ) { + logD("onTransitionReady transition=%s", transition) // Check if this is a pending external exit transition. val pendingExit = pendingExternalExitTransitions .firstOrNull { pendingExit -> pendingExit.transition == transition } if (pendingExit != null) { - pendingExternalExitTransitions.remove(pendingExit) if (info.hasTaskChange(taskId = pendingExit.taskId)) { if (desktopRepository.isTaskInFullImmersiveState(pendingExit.taskId)) { - logV("Pending external exit for task ${pendingExit.taskId} verified") + logV("Pending external exit for task#%d verified", pendingExit.taskId) desktopRepository.setTaskInFullImmersiveState( displayId = pendingExit.displayId, taskId = pendingExit.taskId, @@ -311,7 +349,7 @@ class DesktopFullImmersiveTransitionHandler( val state = requireState() val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId } .startAbsBounds - logV("Direct move for task ${state.taskId} in ${state.direction} direction verified") + logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction) when (state.direction) { Direction.ENTER -> { desktopRepository.setTaskInFullImmersiveState( @@ -343,7 +381,7 @@ class DesktopFullImmersiveTransitionHandler( .filter { c -> desktopRepository.isTaskInFullImmersiveState(c.taskInfo!!.taskId) } .filter { c -> c.startRotation != c.endRotation } .forEach { c -> - logV("Detected immersive exit due to rotation for task: ${c.taskInfo!!.taskId}") + logV("Detected immersive exit due to rotation for task#%d", c.taskInfo!!.taskId) desktopRepository.setTaskInFullImmersiveState( displayId = c.taskInfo!!.displayId, taskId = c.taskInfo!!.taskId, @@ -352,6 +390,32 @@ class DesktopFullImmersiveTransitionHandler( } } + override fun onTransitionMerged(merged: IBinder, playing: IBinder) { + logD("onTransitionMerged merged=%s playing=%s", merged, playing) + val pendingExit = pendingExternalExitTransitions + .firstOrNull { pendingExit -> pendingExit.transition == merged } + if (pendingExit != null) { + logV( + "Pending exit transition %s for task#%s merged into %s", + merged, pendingExit.taskId, playing + ) + pendingExit.transition = playing + } + } + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + logD("onTransitionFinished transition=%s aborted=%b", transition, aborted) + val pendingExit = pendingExternalExitTransitions + .firstOrNull { pendingExit -> pendingExit.transition == transition } + if (pendingExit != null) { + logV( + "Pending exit transition %s for task#%s finished", + transition, pendingExit + ) + pendingExternalExitTransitions.remove(pendingExit) + } + } + private fun clearState() { state = null } @@ -394,7 +458,7 @@ class DesktopFullImmersiveTransitionHandler( data class ExternalPendingExit( val taskId: Int, val displayId: Int, - val transition: IBinder, + var transition: IBinder, ) private enum class Direction { @@ -405,6 +469,10 @@ class DesktopFullImmersiveTransitionHandler( ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } + private fun logD(msg: String, vararg arguments: Any?) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + private companion object { private const val TAG = "DesktopImmersive" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt index df9fc59b925e..54a07f20fd84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt @@ -23,6 +23,7 @@ import android.os.Handler import android.os.IBinder import android.view.SurfaceControl import android.view.WindowManager +import android.window.DesktopModeFlags import android.window.TransitionInfo import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction @@ -59,6 +60,9 @@ class DesktopMixedTransitionHandler( /** Starts close transition and handles or delegates desktop task close animation. */ override fun startRemoveTransition(wct: WindowContainerTransaction?): IBinder { + if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS.isTrue) { + return freeformTaskTransitionHandler.startRemoveTransition(wct) + } requireNotNull(wct) return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this) } @@ -138,7 +142,7 @@ class DesktopMixedTransitionHandler( private fun isLastDesktopTask(change: TransitionInfo.Change): Boolean = change.taskInfo?.let { - desktopRepository.getActiveNonMinimizedTaskCount(it.displayId) == 1 + desktopRepository.getExpandedTaskCount(it.displayId) == 1 } ?: false private fun findCloseDesktopTaskChange(info: TransitionInfo): TransitionInfo.Change? { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index 8ebe503a3816..255ca6e2511f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -16,11 +16,20 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo +import android.util.Size +import android.view.InputDevice.SOURCE_MOUSE +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.MotionEvent +import android.view.MotionEvent.TOOL_TYPE_FINGER +import android.view.MotionEvent.TOOL_TYPE_MOUSE +import android.view.MotionEvent.TOOL_TYPE_STYLUS import com.android.internal.annotations.VisibleForTesting import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags +import com.android.wm.shell.common.DisplayController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import java.security.SecureRandom import java.util.Random @@ -176,7 +185,13 @@ class DesktopModeEventLogger { * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode * session. */ - fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingStarted( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ) { if (!Flags.enableResizingMetrics()) return val sessionId = currentSessionId.get() @@ -188,11 +203,19 @@ class DesktopModeEventLogger { return } + val taskSizeUpdate = createTaskSizeUpdate( + resizeTrigger, + motionEvent, + taskInfo, + displayController = displayController, + displayLayoutSize = displayLayoutSize, + ) + ProtoLog.v( WM_SHELL_DESKTOP_MODE, - "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s", + "DesktopModeLogger: Logging task resize is starting, session: %s, taskSizeUpdate: %s", sessionId, - taskSizeUpdate.instanceId + taskSizeUpdate ) logTaskSizeUpdated( FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE, @@ -203,7 +226,15 @@ class DesktopModeEventLogger { /** * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session. */ - fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingEnded( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + taskHeight: Int? = null, + taskWidth: Int? = null, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ) { if (!Flags.enableResizingMetrics()) return val sessionId = currentSessionId.get() @@ -215,18 +246,61 @@ class DesktopModeEventLogger { return } + val taskSizeUpdate = createTaskSizeUpdate( + resizeTrigger, + motionEvent, + taskInfo, + taskHeight, + taskWidth, + displayController, + displayLayoutSize, + ) + ProtoLog.v( WM_SHELL_DESKTOP_MODE, - "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s", + "DesktopModeLogger: Logging task resize is ending, session: %s, taskSizeUpdate: %s", sessionId, - taskSizeUpdate.instanceId + taskSizeUpdate ) + logTaskSizeUpdated( FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE, sessionId, taskSizeUpdate ) } + private fun createTaskSizeUpdate( + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + taskInfo: RunningTaskInfo, + taskHeight: Int? = null, + taskWidth: Int? = null, + displayController: DisplayController? = null, + displayLayoutSize: Size? = null, + ): TaskSizeUpdate { + val taskBounds = taskInfo.configuration.windowConfiguration.bounds + + val height = taskHeight ?: taskBounds.height() + val width = taskWidth ?: taskBounds.width() + + val displaySize = when { + displayLayoutSize != null -> displayLayoutSize.height * displayLayoutSize.width + displayController != null -> displayController.getDisplayLayout(taskInfo.displayId) + ?.let { it.height() * it.width() } + else -> null + } + + return TaskSizeUpdate( + resizeTrigger, + getInputMethodFromMotionEvent(motionEvent), + taskInfo.taskId, + taskInfo.effectiveUid, + height, + width, + displaySize, + ) + } + fun logTaskInfoStateInit() { logTaskUpdate( FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD, @@ -238,7 +312,8 @@ class DesktopModeEventLogger { taskHeight = 0, taskWidth = 0, taskX = 0, - taskY = 0) + taskY = 0 + ) ) } @@ -314,7 +389,7 @@ class DesktopModeEventLogger { /* task_width */ taskSizeUpdate.taskWidth, /* display_area */ - taskSizeUpdate.displayArea + taskSizeUpdate.displayArea ?: -1 ) } @@ -364,9 +439,24 @@ class DesktopModeEventLogger { val uid: Int, val taskHeight: Int, val taskWidth: Int, - val displayArea: Int, + val displayArea: Int?, ) + private fun getInputMethodFromMotionEvent(e: MotionEvent?): InputMethod { + if (e == null) return InputMethod.UNKNOWN_INPUT_METHOD + + val toolType = e.getToolType( + e.findPointerIndex(e.getPointerId(0)) + ) + return when { + toolType == TOOL_TYPE_STYLUS -> InputMethod.STYLUS + toolType == TOOL_TYPE_MOUSE -> InputMethod.MOUSE + toolType == TOOL_TYPE_FINGER && e.source == SOURCE_MOUSE -> InputMethod.TOUCHPAD + toolType == TOOL_TYPE_FINGER && e.source == SOURCE_TOUCHSCREEN -> InputMethod.TOUCH + else -> InputMethod.UNKNOWN_INPUT_METHOD + } + } + // Default value used when the task was not minimized. @VisibleForTesting const val UNSET_MINIMIZE_REASON = @@ -499,6 +589,10 @@ class DesktopModeEventLogger { FrameworkStatsLog .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__SNAP_RIGHT_MENU_RESIZE_TRIGGER ), + MAXIMIZE_MENU( + FrameworkStatsLog + .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__MAXIMIZE_MENU_RESIZE_TRIGGER + ), } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index 443e4179b524..6f7b7162d2ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -30,7 +30,6 @@ import androidx.core.util.valueIterator import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository -import com.android.wm.shell.desktopmode.persistence.DesktopTask import com.android.wm.shell.desktopmode.persistence.DesktopTaskState import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread @@ -124,26 +123,30 @@ class DesktopRepository ( if (!Flags.enableDesktopWindowingPersistence()) return // TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized mainCoroutineScope.launch { - val desktop = persistentRepository.readDesktop() + val desktop = persistentRepository.readDesktop() ?: return@launch + val maxTasks = DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 } ?: desktop.zOrderedTasksCount + var visibleTasksCount = 0 desktop.zOrderedTasksList // Reverse it so we initialize the repo from bottom to top. .reversed() - .map { taskId -> - desktop.tasksByTaskIdMap.getOrDefault( - taskId, - DesktopTask.getDefaultInstance() - ) - } - .filter { task -> task.desktopTaskState == DesktopTaskState.VISIBLE } - .take(maxTasks) + .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] } .forEach { task -> - addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId) - addActiveTask(desktop.displayId, task.taskId) - updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + if (task.desktopTaskState == DesktopTaskState.VISIBLE + && visibleTasksCount < maxTasks + ) { + visibleTasksCount++ + addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId) + addActiveTask(desktop.displayId, task.taskId) + updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + } else { + addActiveTask(desktop.displayId, task.taskId) + updateTaskVisibility(desktop.displayId, task.taskId, visible = false) + minimizeTask(desktop.displayId, task.taskId) + } } } } @@ -262,11 +265,11 @@ class DesktopRepository ( ArraySet(desktopTaskDataByDisplayId[displayId]?.minimizedTasks) /** Returns all active non-minimized tasks for [displayId] ordered from top to bottom. */ - fun getActiveNonMinimizedOrderedTasks(displayId: Int): List<Int> = + fun getExpandedTasksOrdered(displayId: Int): List<Int> = getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) } /** Returns the count of active non-minimized tasks for [displayId]. */ - fun getActiveNonMinimizedTaskCount(displayId: Int): Int { + fun getExpandedTaskCount(displayId: Int): Int { return getActiveTasks(displayId).count { !isMinimizedTask(it) } } 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 b505bee469fe..bc78e43a15ad 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 @@ -46,7 +46,9 @@ import android.util.Size import android.view.Display.DEFAULT_DISPLAY import android.view.DragEvent import android.view.KeyEvent +import android.view.MotionEvent import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_NONE @@ -58,6 +60,7 @@ import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVI import android.window.DesktopModeFlags.ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS import android.window.RemoteTransition import android.window.TransitionInfo +import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.annotation.BinderThread @@ -114,6 +117,7 @@ import com.android.wm.shell.sysui.UserChangeListener import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TransitionFinishCallback import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener @@ -125,7 +129,7 @@ import java.io.PrintWriter import java.util.Optional import java.util.concurrent.Executor import java.util.function.Consumer - +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger /** Handles moving tasks in and out of desktop */ class DesktopTasksController( private val context: Context, @@ -145,7 +149,7 @@ class DesktopTasksController( private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler, private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler, private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler, - private val immersiveTransitionHandler: DesktopFullImmersiveTransitionHandler, + private val desktopImmersiveController: DesktopImmersiveController, private val taskRepository: DesktopRepository, private val desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver, private val launchAdjacentController: LaunchAdjacentController, @@ -158,6 +162,7 @@ class DesktopTasksController( @ShellMainThread private val handler: Handler, private val inputManager: InputManager, private val focusTransitionObserver: FocusTransitionObserver, + private val desktopModeEventLogger: DesktopModeEventLogger, ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler, @@ -250,7 +255,7 @@ class DesktopTasksController( toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener) dragToDesktopTransitionHandler.onTaskResizeAnimationListener = listener - immersiveTransitionHandler.onTaskResizeAnimationListener = listener + desktopImmersiveController.onTaskResizeAnimationListener = listener } fun setOnTaskRepositionAnimationListener(listener: OnTaskRepositionAnimationListener) { @@ -368,10 +373,13 @@ class DesktopTasksController( } logV("moveBackgroundTaskToDesktop with taskId=%d", taskId) // TODO(342378842): Instead of using default display, support multiple displays - val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( DEFAULT_DISPLAY, wct, taskId) - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = taskId, + ) wct.startTask( taskId, ActivityOptions.makeBasic().apply { @@ -380,7 +388,7 @@ class DesktopTasksController( ) // TODO(343149901): Add DPI changes for task launch val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) return true } @@ -398,14 +406,18 @@ class DesktopTasksController( } logV("moveRunningTaskToDesktop taskId=%d", task.taskId) exitSplitIfApplicable(wct, task) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, task.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = task.displayId, + excludeTaskId = task.taskId, + ) // Bring other apps to front first - val taskToMinimize = + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId) addMoveToDesktopChanges(wct, task) val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) } @@ -440,14 +452,14 @@ class DesktopTasksController( val wct = WindowContainerTransaction() exitSplitIfApplicable(wct, taskInfo) moveHomeTask(wct, toTop = true) - val taskToMinimize = + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId) addMoveToDesktopChanges(wct, taskInfo) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable( + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( wct, taskInfo.displayId) val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct) transition?.let { - addPendingMinimizeTransition(it, taskToMinimize) + taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) } runOnTransit?.invoke(transition) } } @@ -490,7 +502,7 @@ class DesktopTasksController( taskId ) ) - return immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo) + return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) } fun minimizeTask(taskInfo: RunningTaskInfo) { @@ -503,7 +515,7 @@ class DesktopTasksController( removeWallpaperActivity(wct) } // Notify immersive handler as it might need to exit immersive state. - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, taskInfo) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo) wct.reorder(taskInfo.token, false) val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct) @@ -607,8 +619,11 @@ class DesktopTasksController( logV("moveBackgroundTaskToFront taskId=%s", taskId) val wct = WindowContainerTransaction() // TODO: b/342378842 - Instead of using default display, support multiple displays - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = taskId, + ) wct.startTask( taskId, ActivityOptions.makeBasic().apply { @@ -630,10 +645,19 @@ class DesktopTasksController( logV("moveTaskToFront taskId=%s", taskInfo.taskId) val wct = WindowContainerTransaction() wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */) - val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable( - wct, taskInfo.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = taskInfo.displayId, + excludeTaskId = taskInfo.taskId, + ) val transition = - startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition) + startLaunchTransition( + TRANSIT_TO_FRONT, + wct, + taskInfo.taskId, + remoteTransition, + taskInfo.displayId + ) runOnTransit?.invoke(transition) } @@ -642,15 +666,15 @@ class DesktopTasksController( wct: WindowContainerTransaction, taskId: Int, remoteTransition: RemoteTransition?, + displayId: Int = DEFAULT_DISPLAY, ): IBinder { - val taskToMinimize: RunningTaskInfo? = - addAndGetMinimizeChangesIfNeeded(DEFAULT_DISPLAY, wct, taskId) + val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId) if (remoteTransition == null) { val t = transitions.startTransition(transitionType, wct, null /* handler */) - addPendingMinimizeTransition(t, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } return t } - if (taskToMinimize == null) { + if (taskIdToMinimize == null) { val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition) val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) remoteTransitionHandler.setTransition(t) @@ -658,10 +682,10 @@ class DesktopTasksController( } val remoteTransitionHandler = DesktopWindowLimitRemoteHandler( - mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId) + mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize) val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) remoteTransitionHandler.setTransition(t) - addPendingMinimizeTransition(t, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } return t } @@ -734,12 +758,12 @@ class DesktopTasksController( private fun moveDesktopTaskToFullImmersive(taskInfo: RunningTaskInfo) { check(taskInfo.isFreeform) { "Task must already be in freeform" } - immersiveTransitionHandler.moveTaskToImmersive(taskInfo) + desktopImmersiveController.moveTaskToImmersive(taskInfo) } private fun exitDesktopTaskFromFullImmersive(taskInfo: RunningTaskInfo) { check(taskInfo.isFreeform) { "Task must already be in freeform" } - immersiveTransitionHandler.moveTaskToNonImmersive(taskInfo) + desktopImmersiveController.moveTaskToNonImmersive(taskInfo) } /** @@ -747,7 +771,11 @@ class DesktopTasksController( * bounds) and a free floating state (either the last saved bounds if available or the default * bounds otherwise). */ - fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) { + fun toggleDesktopTaskSize( + taskInfo: RunningTaskInfo, + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, + ) { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } @@ -794,7 +822,10 @@ class DesktopTasksController( taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) - + desktopModeEventLogger.logTaskResizingEnded( + resizeTrigger, motionEvent, taskInfo, destinationBounds.height(), + destinationBounds.width(), displayController + ) toggleResizeDesktopTaskTransitionHandler.startTransition(wct) } @@ -847,7 +878,7 @@ class DesktopTasksController( excludeTaskId: Int? = null, ): Boolean { val doesAnyTaskRequireTaskbarRounding = - taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + taskRepository.getExpandedTasksOrdered(displayId) // exclude current task since maximize/restore transition has not taken place yet. .filterNot { taskId -> taskId == excludeTaskId } .any { taskId -> @@ -884,9 +915,19 @@ class DesktopTasksController( taskInfo: RunningTaskInfo, taskSurface: SurfaceControl, currentDragBounds: Rect, - position: SnapPosition + position: SnapPosition, + resizeTrigger: ResizeTrigger, + motionEvent: MotionEvent?, ) { val destinationBounds = getSnapBounds(taskInfo, position) + desktopModeEventLogger.logTaskResizingEnded( + resizeTrigger, + motionEvent, + taskInfo, + destinationBounds.height(), + destinationBounds.width(), + displayController, + ) if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) { // Handle the case where we attempt to snap resize when already snap resized: the task // position won't need to change but we want to animate the surface going back to the @@ -915,7 +956,8 @@ class DesktopTasksController( position: SnapPosition, taskSurface: SurfaceControl, currentDragBounds: Rect, - dragStartBounds: Rect + dragStartBounds: Rect, + motionEvent: MotionEvent, ) { releaseVisualIndicator() if (!taskInfo.isResizeable && DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE.isTrue()) { @@ -932,10 +974,25 @@ class DesktopTasksController( isResizable = taskInfo.isResizeable, ) } else { + val resizeTrigger = if (position == SnapPosition.LEFT) { + ResizeTrigger.DRAG_LEFT + } else { + ResizeTrigger.DRAG_RIGHT + } + desktopModeEventLogger.logTaskResizingStarted( + resizeTrigger, motionEvent, taskInfo, displayController + ) interactionJankMonitor.begin( taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable" ) - snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position) + snapToHalfScreen( + taskInfo, + taskSurface, + currentDragBounds, + position, + resizeTrigger, + motionEvent, + ) } } @@ -981,13 +1038,13 @@ class DesktopTasksController( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int - ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront) + ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront) private fun bringDesktopAppsToFront( displayId: Int, wct: WindowContainerTransaction, newTaskIdInFront: Int? = null - ): RunningTaskInfo? { + ): Int? { logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront) // Move home to front, ensures that we go back home when all desktop windows are closed moveHomeTask(wct, toTop = true) @@ -999,25 +1056,24 @@ class DesktopTasksController( addWallpaperActivity(wct) } - val nonMinimizedTasksOrderedFrontToBack = - taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + val expandedTasksOrderedFrontToBack = + taskRepository.getExpandedTasksOrdered(displayId) // If we're adding a new Task we might need to minimize an old one // TODO(b/365725441): Handle non running task minimization - val taskToMinimize: RunningTaskInfo? = + val taskIdToMinimize: Int? = if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) { - desktopTasksLimiter - .get() - .getTaskToMinimizeIfNeeded( - nonMinimizedTasksOrderedFrontToBack, + desktopTasksLimiter.get() + .getTaskIdToMinimize( + expandedTasksOrderedFrontToBack, newTaskIdInFront ) } else { null } - nonMinimizedTasksOrderedFrontToBack + expandedTasksOrderedFrontToBack // If there is a Task to minimize, let it stay behind the Home Task - .filter { taskId -> taskId != taskToMinimize?.taskId } + .filter { taskId -> taskId != taskIdToMinimize } .reversed() // Start from the back so the front task is brought forward last .forEach { taskId -> val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId) @@ -1038,7 +1094,7 @@ class DesktopTasksController( taskbarDesktopTaskListener?. onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId)) - return taskToMinimize + return taskIdToMinimize } private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) { @@ -1185,6 +1241,67 @@ class DesktopTasksController( return result } + /** Whether the given [change] in the [transition] is a known desktop change. */ + fun isDesktopChange( + transition: IBinder, + change: TransitionInfo.Change, + ): Boolean { + // Only the immersive controller is currently involved in mixed transitions. + return Flags.enableFullyImmersiveInDesktop() + && desktopImmersiveController.isImmersiveChange(transition, change) + } + + /** + * Whether the given transition [info] will potentially include a desktop change, in which + * case the transition should be treated as mixed so that the change is in part animated by + * one of the desktop transition handlers. + */ + fun shouldPlayDesktopAnimation(info: TransitionRequestInfo): Boolean { + // Only immersive mixed transition are currently supported. + if (!Flags.enableFullyImmersiveInDesktop()) return false + val triggerTask = info.triggerTask ?: return false + if (!isDesktopModeShowing(triggerTask.displayId)) { + return false + } + if (!TransitionUtil.isOpeningType(info.type)) { + return false + } + taskRepository.getTaskInFullImmersiveState(displayId = triggerTask.displayId) + ?: return false + return when { + triggerTask.isFullscreen -> { + // Trigger fullscreen task will enter desktop, so any existing immersive task + // should exit. + shouldFullscreenTaskLaunchSwitchToDesktop(triggerTask) + } + triggerTask.isFreeform -> { + // Trigger freeform task will enter desktop, so any existing immersive task should + // exit. + !shouldFreeformTaskLaunchSwitchToFullscreen(triggerTask) + } + else -> false + } + } + + /** Animate a desktop change found in a mixed transitions. */ + fun animateDesktopChange( + transition: IBinder, + change: Change, + startTransaction: Transaction, + finishTransaction: Transaction, + finishCallback: TransitionFinishCallback, + ) { + if (!desktopImmersiveController.isImmersiveChange(transition, change)) { + throw IllegalStateException("Only immersive changes support desktop mixed transitions") + } + desktopImmersiveController.animateResizeChange( + change, + startTransaction, + finishTransaction, + finishCallback + ) + } + private fun taskContainsDragAndDropCookie(taskInfo: RunningTaskInfo?) = taskInfo?.launchCookies?.any { it == dragAndDropFullscreenCookie } ?: false @@ -1229,12 +1346,15 @@ class DesktopTasksController( val options = createNewWindowOptions(callingTask) if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) { wct.startTask(requestedTaskId, options.toBundle()) - val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( + val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( callingTask.displayId, wct, requestedTaskId) - val runOnTransit = immersiveTransitionHandler - .exitImmersiveIfApplicable(wct, callingTask.displayId) + val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable( + wct = wct, + displayId = callingTask.displayId, + excludeTaskId = requestedTaskId, + ) val transition = transitions.startTransition(TRANSIT_OPEN, wct, null) - addPendingMinimizeTransition(transition, taskToMinimize) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } runOnTransit?.invoke(transition) } else { val splitPosition = splitScreenController.determineNewInstancePosition(callingTask) @@ -1341,7 +1461,7 @@ class DesktopTasksController( return null } val wct = WindowContainerTransaction() - if (!isDesktopModeShowing(task.displayId)) { + if (shouldFreeformTaskLaunchSwitchToFullscreen(task)) { logD("Bring desktop tasks to front on transition=taskId=%d", task.taskId) if (taskRepository.isActiveTask(task.taskId) && !forceEnterDesktop(task.displayId)) { // We are outside of desktop mode and already existing desktop task is being @@ -1372,11 +1492,11 @@ class DesktopTasksController( } // Desktop Mode is showing and we're launching a new Task: // 1) Exit immersive if needed. - immersiveTransitionHandler.exitImmersiveIfApplicable(transition, wct, task.displayId) + desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId) // 2) minimize a Task if needed. - val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task.taskId) - if (taskToMinimize != null) { - addPendingMinimizeTransition(transition, taskToMinimize) + val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) + if (taskIdToMinimize != null) { + addPendingMinimizeTransition(transition, taskIdToMinimize) return wct } return if (wct.isEmpty) null else wct @@ -1387,7 +1507,7 @@ class DesktopTasksController( transition: IBinder ): WindowContainerTransaction? { logV("handleFullscreenTaskLaunch") - if (isDesktopModeShowing(task.displayId) || forceEnterDesktop(task.displayId)) { + if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) { logD("Switch fullscreen task to freeform on transition: taskId=%d", task.taskId) return WindowContainerTransaction().also { wct -> addMoveToDesktopChanges(wct, task) @@ -1400,10 +1520,9 @@ class DesktopTasksController( // Desktop Mode is already showing and we're launching a new Task - we might need to // minimize another Task. - val taskToMinimize = - addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task.taskId) - addPendingMinimizeTransition(transition, taskToMinimize) - immersiveTransitionHandler.exitImmersiveIfApplicable( + val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) + taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } + desktopImmersiveController.exitImmersiveIfApplicable( transition, wct, task.displayId ) } @@ -1411,6 +1530,12 @@ class DesktopTasksController( return null } + private fun shouldFreeformTaskLaunchSwitchToFullscreen(task: RunningTaskInfo): Boolean = + !isDesktopModeShowing(task.displayId) + + private fun shouldFullscreenTaskLaunchSwitchToDesktop(task: RunningTaskInfo): Boolean = + isDesktopModeShowing(task.displayId) || forceEnterDesktop(task.displayId) + /** * If a task is not compatible with desktop mode freeform, it should always be launched in * fullscreen. @@ -1529,7 +1654,7 @@ class DesktopTasksController( val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) - val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + val activeTasks = taskRepository.getExpandedTasksOrdered(displayId) activeTasks.firstOrNull()?.let { activeTask -> shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let { cascadeWindow(context.resources, stableBounds, @@ -1558,22 +1683,22 @@ class DesktopTasksController( } /** Returns the ID of the Task that will be minimized, or null if no task will be minimized. */ - private fun addAndGetMinimizeChangesIfNeeded( + private fun addAndGetMinimizeChanges( displayId: Int, wct: WindowContainerTransaction, newTaskId: Int - ): RunningTaskInfo? { + ): Int? { if (!desktopTasksLimiter.isPresent) return null return desktopTasksLimiter .get() - .addAndGetMinimizeTaskChangesIfNeeded(displayId, wct, newTaskId) + .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId) } private fun addPendingMinimizeTransition( transition: IBinder, - taskToMinimize: RunningTaskInfo? + taskIdToMinimize: Int, ) { - if (taskToMinimize == null) return + val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return desktopTasksLimiter.ifPresent { it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId) } @@ -1735,6 +1860,7 @@ class DesktopTasksController( currentDragBounds: Rect, validDragArea: Rect, dragStartBounds: Rect, + motionEvent: MotionEvent, ) { if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return @@ -1755,12 +1881,22 @@ class DesktopTasksController( } IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { handleSnapResizingTask( - taskInfo, SnapPosition.LEFT, taskSurface, currentDragBounds, dragStartBounds + taskInfo, + SnapPosition.LEFT, + taskSurface, + currentDragBounds, + dragStartBounds, + motionEvent, ) } IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { handleSnapResizingTask( - taskInfo, SnapPosition.RIGHT, taskSurface, currentDragBounds, dragStartBounds + taskInfo, + SnapPosition.RIGHT, + taskSurface, + currentDragBounds, + dragStartBounds, + motionEvent, ) } IndicatorType.NO_INDICATOR -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt index d6b721253abf..7bcc5d1691aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.desktopmode -import android.app.ActivityManager.RunningTaskInfo import android.content.Context import android.os.Handler import android.os.IBinder @@ -30,7 +29,7 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW import com.android.internal.jank.InteractionJankMonitor import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionObserver @@ -57,13 +56,11 @@ class DesktopTasksLimiter ( init { require(maxTasksLimit > 0) { - "DesktopTasksLimiter should not be created with a maxTasksLimit at 0 or less. " + - "Current value: $maxTasksLimit." + "DesktopTasksLimiter: maxTasksLimit should be greater than 0. Current value: $maxTasksLimit." } transitions.registerObserver(minimizeTransitionObserver) taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover) - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: starting limiter with a maximum of %d tasks", maxTasksLimit) + logV("Starting limiter with a maximum of %d tasks", maxTasksLimit) } private data class TaskDetails( @@ -88,20 +85,14 @@ class DesktopTasksLimiter ( finishTransaction: SurfaceControl.Transaction ) { val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return - if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return - - if (!isTaskReorderedToBackOrInvisible(info, taskToMinimize)) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: task %d is not reordered to back nor invis", - taskToMinimize.taskId) + if (!isTaskReadyForMinimize(info, taskToMinimize)) { + logV("task %d is not reordered to back nor invis", taskToMinimize.taskId) return } - taskToMinimize.transitionInfo = info activeTransitionTokensAndTasks[transition] = taskToMinimize - this@DesktopTasksLimiter.markTaskMinimized( + this@DesktopTasksLimiter.minimizeTask( taskToMinimize.displayId, taskToMinimize.taskId) } @@ -109,18 +100,15 @@ class DesktopTasksLimiter ( * Returns whether the Task [taskDetails] is being reordered to the back in the transition * [info], or is already invisible. * - * This check can be used to double-check that a task was indeed minimized before - * marking it as such. + * This check confirms a task should be minimized before minimizing it. */ - private fun isTaskReorderedToBackOrInvisible( - info: TransitionInfo, - taskDetails: TaskDetails + private fun isTaskReadyForMinimize( + info: TransitionInfo, + taskDetails: TaskDetails ): Boolean { val taskChange = info.changes.find { change -> change.taskInfo?.taskId == taskDetails.taskId } - if (taskChange == null) { - return !taskRepository.isVisibleTask(taskDetails.taskId) - } + if (taskChange == null) return !taskRepository.isVisibleTask(taskDetails.taskId) return taskChange.mode == TRANSIT_TO_BACK } @@ -145,9 +133,7 @@ class DesktopTasksLimiter ( } override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: transition %s finished", transition) + logV("transition %s finished", transition) if (activeTransitionTokensAndTasks.remove(transition) != null) { if (aborted) { interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW) @@ -170,18 +156,11 @@ class DesktopTasksLimiter ( } fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) { - if (taskRepository.getActiveNonMinimizedOrderedTasks(displayId).isNotEmpty()) { - return - } + if (taskRepository.getExpandedTasksOrdered(displayId).isNotEmpty()) return val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId) - if (remainingMinimizedTasks.isEmpty()) { - return - } - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: removing leftover minimized tasks: %s", - remainingMinimizedTasks, - ) + if (remainingMinimizedTasks.isEmpty()) return + + logV("Removing leftover minimized tasks: %s", remainingMinimizedTasks) remainingMinimizedTasks.forEach { taskIdToRemove -> val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove) if (taskToRemove != null) { @@ -192,44 +171,41 @@ class DesktopTasksLimiter ( } /** - * Mark [taskId], which must be on [displayId], as minimized, this should only be done after the - * corresponding transition has finished so we don't minimize the task if the transition fails. + * Mark task with [taskId] on [displayId] as minimized. + * + * This should be after the corresponding transition has finished so we don't + * minimize the task if the transition fails. */ - private fun markTaskMinimized(displayId: Int, taskId: Int) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: marking %d as minimized", taskId) + private fun minimizeTask(displayId: Int, taskId: Int) { + logV("Minimize taskId=%d, displayId=%d", taskId, displayId) taskRepository.minimizeTask(displayId, taskId) } /** - * Add a minimize-transition to [wct] if adding [newFrontTaskInfo] brings us over the task + * Adds a minimize-transition to [wct] if adding [newFrontTaskInfo] crosses task * limit, returning the task to minimize. - * - * The task must be on [displayId]. */ - fun addAndGetMinimizeTaskChangesIfNeeded( + fun addAndGetMinimizeTaskChanges( displayId: Int, wct: WindowContainerTransaction, newFrontTaskId: Int, - ): RunningTaskInfo? { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: addMinimizeBackTaskChangesIfNeeded, newFrontTask=%d", - newFrontTaskId) - val newTaskListOrderedFrontToBack = createOrderedTaskListWithGivenTaskInFront( - taskRepository.getActiveNonMinimizedOrderedTasks(displayId), - newFrontTaskId) - val taskToMinimize = getTaskToMinimizeIfNeeded(newTaskListOrderedFrontToBack) - if (taskToMinimize != null) { - wct.reorder(taskToMinimize.token, false /* onTop */) - return taskToMinimize + ): Int? { + logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId) + + val taskIdToMinimize = + getTaskIdToMinimize( + taskRepository.getExpandedTasksOrdered(displayId), + newFrontTaskId + ) + // If it's a running task, reorder it to back. + taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let { + wct.reorder(it.token, false /* onTop */) } - return null + return taskIdToMinimize } /** - * Add a pending minimize transition change, to update the list of minimized apps once the + * Add a pending minimize transition change to update the list of minimized apps once the * transition goes through. */ fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) { @@ -238,51 +214,49 @@ class DesktopTasksLimiter ( } /** - * Returns the Task to minimize given 1. a list of visible tasks ordered from front to back and - * 2. a new task placed in front of all the others. + * Returns the minimized task from the list of visible tasks ordered from front to back with + * the new task placed in front of other tasks. */ - fun getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack: List<Int>, - newTaskIdInFront: Int - ): RunningTaskInfo? { - return getTaskToMinimizeIfNeeded( - createOrderedTaskListWithGivenTaskInFront( - visibleFreeformTaskIdsOrderedFrontToBack, newTaskIdInFront)) + fun getTaskIdToMinimize( + visibleOrderedTasks: List<Int>, + newTaskIdInFront: Int? = null + ): Int? { + return getTaskIdToMinimize( + createOrderedTaskListWithGivenTaskInFront( + visibleOrderedTasks, newTaskIdInFront)) } /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */ - fun getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack: List<Int> - ): RunningTaskInfo? { - if (visibleFreeformTaskIdsOrderedFrontToBack.size <= maxTasksLimit) { - ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: no need to minimize; tasks below limit") + private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? { + if (visibleOrderedTasks.size <= maxTasksLimit) { + logV("No need to minimize; tasks below limit") // No need to minimize anything return null } - val taskIdToMinimize = visibleFreeformTaskIdsOrderedFrontToBack.last() - val taskToMinimize = - shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) - if (taskToMinimize == null) { - ProtoLog.e( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "DesktopTasksLimiter: taskToMinimize(taskId = %d) == null", - taskIdToMinimize, - ) - return null - } - return taskToMinimize + return visibleOrderedTasks.last() } private fun createOrderedTaskListWithGivenTaskInFront( existingTaskIdsOrderedFrontToBack: List<Int>, - newTaskId: Int + newTaskId: Int? ): List<Int> { - return listOf(newTaskId) + + return if (newTaskId == null) existingTaskIdsOrderedFrontToBack + else listOf(newTaskId) + existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId } } @VisibleForTesting fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver -}
\ No newline at end of file + + private fun logV(msg: String, vararg arguments: Any?) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private fun logE(msg: String, vararg arguments: Any?) { + ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + + private companion object { + const val TAG = "DesktopTasksLimiter" + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt index 334dc5aca19d..f21a124f0b8b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -325,10 +325,15 @@ class AppHandleEducationController( /** * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in * datastore proto object. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That means + * it will emit education has not been viewed yet always. */ private fun isEducationViewedFlow(): Flow<Boolean> = appHandleEducationDatastoreRepository.dataStoreFlow - .map { preferences -> preferences.hasEducationViewedTimestampMillis() } + .map { preferences -> + preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + } .distinctUntilChanged() /** @@ -352,5 +357,10 @@ class AppHandleEducationController( val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) + + val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean + get() = + SystemProperties.getBoolean( + "persist.desktop_windowing_app_handle_education_override_conditions", false) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt index 15f4c249cf22..144370d76060 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -23,10 +23,12 @@ import android.os.SystemClock import android.provider.Settings.Secure import com.android.wm.shell.R import com.android.wm.shell.desktopmode.CaptionState +import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.SHOULD_OVERRIDE_EDUCATION_CONDITIONS import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto import java.time.Duration +@kotlinx.coroutines.ExperimentalCoroutinesApi /** Filters incoming app handle education triggers based on set conditions. */ class AppHandleEducationFilter( private val context: Context, @@ -35,9 +37,16 @@ class AppHandleEducationFilter( private val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - /** Returns true if conditions to show app handle education are met, returns false otherwise. */ + /** + * Returns true if conditions to show app handle education are met, returns false otherwise. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return + * ![captionState.isHandleMenuExpanded]. + */ suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false + if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true + val focusAppPackageName = captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt index 3f41d7cf4e86..2d11e02bd3c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt @@ -73,15 +73,14 @@ class DesktopPersistentRepository( */ private suspend fun getDesktopRepositoryState( userId: Int = DEFAULT_USER_ID - ): DesktopRepositoryState = + ): DesktopRepositoryState? = try { dataStoreFlow .first() - .desktopRepoByUserMap - .getOrDefault(userId, DesktopRepositoryState.getDefaultInstance()) + .desktopRepoByUserMap[userId] } catch (e: Exception) { Log.e(TAG, "Unable to read from datastore", e) - DesktopRepositoryState.getDefaultInstance() + null } /** @@ -91,13 +90,13 @@ class DesktopPersistentRepository( suspend fun readDesktop( userId: Int = DEFAULT_USER_ID, desktopId: Int = DEFAULT_DESKTOP_ID, - ): Desktop = + ): Desktop? = try { val repository = getDesktopRepositoryState(userId) - repository.getDesktopOrThrow(desktopId) + repository?.getDesktopOrThrow(desktopId) } catch (e: Exception) { Log.e(TAG, "Unable to get desktop info from persistent repository", e) - Desktop.getDefaultInstance() + null } /** Adds or updates a desktop stored in the datastore */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java index 771573d48e45..7631ece761b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java @@ -28,7 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.window.flags.Flags; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; @@ -48,7 +48,7 @@ import java.util.Optional; */ public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver { private final Transitions mTransitions; - private final Optional<DesktopFullImmersiveTransitionHandler> mImmersiveTransitionHandler; + private final Optional<DesktopImmersiveController> mDesktopImmersiveController; private final WindowDecorViewModel mWindowDecorViewModel; private final Optional<TaskChangeListener> mTaskChangeListener; private final FocusTransitionObserver mFocusTransitionObserver; @@ -60,12 +60,12 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs Context context, ShellInit shellInit, Transitions transitions, - Optional<DesktopFullImmersiveTransitionHandler> immersiveTransitionHandler, + Optional<DesktopImmersiveController> desktopImmersiveController, WindowDecorViewModel windowDecorViewModel, Optional<TaskChangeListener> taskChangeListener, FocusTransitionObserver focusTransitionObserver) { mTransitions = transitions; - mImmersiveTransitionHandler = immersiveTransitionHandler; + mDesktopImmersiveController = desktopImmersiveController; mWindowDecorViewModel = windowDecorViewModel; mTaskChangeListener = taskChangeListener; mFocusTransitionObserver = focusTransitionObserver; @@ -89,7 +89,8 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs // TODO(b/367268953): Remove when DesktopTaskListener is introduced and the repository // is updated from there **before** the |mWindowDecorViewModel| methods are invoked. // Otherwise window decoration relayout won't run with the immersive state up to date. - mImmersiveTransitionHandler.ifPresent(h -> h.onTransitionReady(transition, info)); + mDesktopImmersiveController.ifPresent(h -> + h.onTransitionReady(transition, info, startT, finishT)); } // Update focus state first to ensure the correct state can be queried from listeners. // TODO(371503964): Remove this once the unified task repository is ready. @@ -194,10 +195,20 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs } @Override - public void onTransitionStarting(@NonNull IBinder transition) {} + public void onTransitionStarting(@NonNull IBinder transition) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionStarting(transition)); + } + } @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionMerged(merged, playing)); + } + final List<ActivityManager.RunningTaskInfo> infoOfMerged = mTransitionToTaskInfo.get(merged); if (infoOfMerged == null) { @@ -218,6 +229,11 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs @Override public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { + if (Flags.enableFullyImmersiveInDesktop()) { + // TODO(b/367268953): Remove when DesktopTaskListener is introduced. + mDesktopImmersiveController.ifPresent(h -> h.onTransitionFinished(transition, aborted)); + } + final List<ActivityManager.RunningTaskInfo> taskInfo = mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList()); mTransitionToTaskInfo.remove(transition); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 89d3dd63a08e..9e9de10d4b1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -15,7 +15,12 @@ */ package com.android.wm.shell.pip.phone; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT; import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT; +import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP; +import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE; import android.annotation.Nullable; import android.content.Context; @@ -23,6 +28,7 @@ import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Looper; import android.view.BatchedInputEventReceiver; @@ -36,6 +42,7 @@ import android.view.ViewConfiguration; import androidx.annotation.VisibleForTesting; +import com.android.internal.policy.TaskResizingAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; @@ -48,6 +55,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import java.io.PrintWriter; import java.util.function.Consumer; +import java.util.function.Function; /** * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to @@ -71,6 +79,7 @@ public class PipResizeGestureHandler { private final PipPinchResizingAlgorithm mPinchResizingAlgorithm; private final int mDisplayId; private final ShellExecutor mMainExecutor; + private final Region mTmpRegion = new Region(); private final PointF mDownPoint = new PointF(); private final PointF mDownSecondPoint = new PointF(); @@ -81,15 +90,24 @@ public class PipResizeGestureHandler { private final Rect mLastResizeBounds = new Rect(); private final Rect mUserResizeBounds = new Rect(); private final Rect mDownBounds = new Rect(); + private final Rect mDragCornerSize = new Rect(); + private final Rect mTmpTopLeftCorner = new Rect(); + private final Rect mTmpTopRightCorner = new Rect(); + private final Rect mTmpBottomLeftCorner = new Rect(); + private final Rect mTmpBottomRightCorner = new Rect(); + private final Rect mDisplayBounds = new Rect(); + private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; private final Consumer<Rect> mUpdateResizeBoundsCallback; + private int mDelta; private float mTouchSlop; private boolean mAllowGesture; private boolean mIsAttached; private boolean mIsEnabled; private boolean mEnablePinchResize; + private boolean mEnableDragCornerResize; private boolean mIsSysUiStateValid; private boolean mThresholdCrossed; private boolean mOngoingPinchToResize = false; @@ -113,7 +131,7 @@ public class PipResizeGestureHandler { PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer, PipDismissTargetHandler pipDismissTargetHandler, - Runnable updateMovementBoundsRunnable, + Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) { mContext = context; @@ -126,6 +144,7 @@ public class PipResizeGestureHandler { mPipTouchState = pipTouchState; mPipTaskOrganizer = pipTaskOrganizer; mPipDismissTargetHandler = pipDismissTargetHandler; + mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; @@ -161,9 +180,20 @@ public class PipResizeGestureHandler { } private void reloadResources() { + final Resources res = mContext.getResources(); + mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size); + mEnableDragCornerResize = res.getBoolean(R.bool.config_pipEnableDragCornerResize); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); } + private void resetDragCorners() { + mDragCornerSize.set(0, 0, mDelta, mDelta); + mTmpTopLeftCorner.set(mDragCornerSize); + mTmpTopRightCorner.set(mDragCornerSize); + mTmpBottomLeftCorner.set(mDragCornerSize); + mTmpBottomRightCorner.set(mDragCornerSize); + } + private void disposeInputChannel() { if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); @@ -211,7 +241,7 @@ public class PipResizeGestureHandler { @VisibleForTesting void onInputEvent(InputEvent ev) { - if (!mEnablePinchResize) { + if (!mEnableDragCornerResize && !mEnablePinchResize) { // No need to handle anything if neither form of resizing is enabled. return; } @@ -239,6 +269,8 @@ public class PipResizeGestureHandler { if (mEnablePinchResize && mOngoingPinchToResize) { onPinchResize(mv); + } else if (mEnableDragCornerResize) { + onDragCornerResize(mv); } } } @@ -250,6 +282,48 @@ public class PipResizeGestureHandler { return mCtrlType != CTRL_NONE || mOngoingPinchToResize; } + /** + * Check whether the current x,y coordinate is within the region in which drag-resize should + * start. + * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which + * overlaps with the PIP window while the rest goes outside of the PIP window. + * _ _ _ _ + * |_|_|_________|_|_| + * |_|_| |_|_| + * | PIP | + * | WINDOW | + * _|_ _|_ + * |_|_|_________|_|_| + * |_|_| |_|_| + */ + public boolean isWithinDragResizeRegion(int x, int y) { + if (!mEnableDragCornerResize) { + return false; + } + + final Rect currentPipBounds = mPipBoundsState.getBounds(); + if (currentPipBounds == null) { + return false; + } + resetDragCorners(); + mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.top - mDelta / 2); + mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2, + currentPipBounds.bottom - mDelta / 2); + + mTmpRegion.setEmpty(); + mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION); + mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION); + + return mTmpRegion.contains(x, y); + } + public boolean isUsingPinchToZoom() { return mEnablePinchResize; } @@ -260,17 +334,62 @@ public class PipResizeGestureHandler { public boolean willStartResizeGesture(MotionEvent ev) { if (isInValidSysUiState()) { - if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - if (mEnablePinchResize && ev.getPointerCount() == 2) { - onPinchResize(ev); - mOngoingPinchToResize = mAllowGesture; - return mAllowGesture; - } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) { + return true; + } + break; + + case MotionEvent.ACTION_POINTER_DOWN: + if (mEnablePinchResize && ev.getPointerCount() == 2) { + onPinchResize(ev); + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; + } + break; + + default: + break; } } return false; } + private void setCtrlType(int x, int y) { + final Rect currentPipBounds = mPipBoundsState.getBounds(); + + Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds); + + mDisplayBounds.set(movementBounds.left, + movementBounds.top, + movementBounds.right + currentPipBounds.width(), + movementBounds.bottom + currentPipBounds.height()); + + if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.left != mDisplayBounds.left) { + mCtrlType |= CTRL_LEFT; + mCtrlType |= CTRL_TOP; + } + if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top + && currentPipBounds.right != mDisplayBounds.right) { + mCtrlType |= CTRL_RIGHT; + mCtrlType |= CTRL_TOP; + } + if (mTmpBottomRightCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.right != mDisplayBounds.right) { + mCtrlType |= CTRL_RIGHT; + mCtrlType |= CTRL_BOTTOM; + } + if (mTmpBottomLeftCorner.contains(x, y) + && currentPipBounds.bottom != mDisplayBounds.bottom + && currentPipBounds.left != mDisplayBounds.left) { + mCtrlType |= CTRL_LEFT; + mCtrlType |= CTRL_BOTTOM; + } + } + private boolean isInValidSysUiState() { return mIsSysUiStateValid; } @@ -364,6 +483,59 @@ public class PipResizeGestureHandler { } } + private void onDragCornerResize(MotionEvent ev) { + int action = ev.getActionMasked(); + float x = ev.getX(); + float y = ev.getY() - mOhmOffset; + if (action == MotionEvent.ACTION_DOWN) { + mLastResizeBounds.setEmpty(); + mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y); + if (mAllowGesture) { + setCtrlType((int) x, (int) y); + mDownPoint.set(x, y); + mDownBounds.set(mPipBoundsState.getBounds()); + } + } else if (mAllowGesture) { + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + // We do not support multi touch for resizing via drag + mAllowGesture = false; + break; + case MotionEvent.ACTION_MOVE: + // Capture inputs + if (!mThresholdCrossed + && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) { + mThresholdCrossed = true; + // Reset the down to begin resizing from this point + mDownPoint.set(x, y); + mInputMonitor.pilferPointers(); + } + if (mThresholdCrossed) { + if (mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE, + false /* resize */); + } + final Rect currentPipBounds = mPipBoundsState.getBounds(); + mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y, + mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, + mMinSize.y, mMaxSize, true, + mDownBounds.width() > mDownBounds.height())); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds, + mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */, + true /* useCurrentSize */); + mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds, + null); + mPipBoundsState.setHasUserResizedPip(true); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + finishResize(); + break; + } + } + } + private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) { final int leftEdge = bounds.left; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 9c4e723efc23..f4c2a33079ba 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -213,7 +213,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler, - this::updateMovementBounds, pipUiEventLogger, + this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor, mPipPerfHintController); mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState, mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 0ed5079b7fba..8ac7f89d8f8a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -476,6 +476,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal } mPipTaskOrganizer.removePip(); mTvPipMenuController.closeMenu(); + mPipNotificationController.dismiss(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index f7ed1dd4606b..6d4d4b410be8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -585,7 +585,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.cancelSyntheticTransition reason=%s", + "[%d] RecentsController.cancelSyntheticTransition: reason=%s", mInstanceId, reason); try { // TODO(b/366021931): Notify the correct tasks once we build actual targets, and @@ -602,13 +602,24 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, * Called when a synthetic transition is finished. * @return */ - boolean finishSyntheticTransition() { + boolean finishSyntheticTransition(IResultReceiver runnerFinishCb, String reason) { if (!isSyntheticTransition()) { return false; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.finishSyntheticTransition", mInstanceId); + "[%d] RecentsController.finishSyntheticTransition: reason=%s", mInstanceId, + reason); + if (runnerFinishCb != null) { + try { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: calling finish callback", + mInstanceId); + runnerFinishCb.send(0, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report transition finished", e); + } + } // TODO(b/366021931): Clean up leashes accordingly cleanUp(); return true; @@ -1230,7 +1241,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, private void finishInner(boolean toHome, boolean sendUserLeaveHint, IResultReceiver runnerFinishCb, String reason) { - if (finishSyntheticTransition()) { + if (finishSyntheticTransition(runnerFinishCb, reason)) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 766a6b3f48ac..0d89f757903e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -83,8 +83,11 @@ public class DefaultMixedHandler implements MixedTransitionHandler, /** Both the display and split-state (enter/exit) is changing */ static final int TYPE_DISPLAY_AND_SPLIT_CHANGE = 2; - /** Pip was entered while handling an intent with its own remoteTransition. */ - static final int TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE = 3; + /** + * While handling an intent with its own remoteTransition, a PIP enter or Desktop immersive + * exit change is found. + */ + static final int TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE = 3; /** Recents transition while split-screen foreground. */ static final int TYPE_RECENTS_DURING_SPLIT = 4; @@ -110,6 +113,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler, /** The display changes when pip is entering. */ static final int TYPE_ENTER_PIP_WITH_DISPLAY_CHANGE = 11; + /** Open transition during a desktop session. */ + static final int TYPE_OPEN_IN_DESKTOP = 12; + /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -296,7 +302,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler, return null; } final MixedTransition mixed = createDefaultMixedTransition( - MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition); + MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE, transition); mixed.mLeftoversHandler = handler.first; mActiveTransitions.add(mixed); if (mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { @@ -334,6 +340,20 @@ public class DefaultMixedHandler implements MixedTransitionHandler, MixedTransition.TYPE_UNFOLD, transition)); } return wct; + } else if (mDesktopTasksController != null + && mDesktopTasksController.shouldPlayDesktopAnimation(request)) { + final Pair<Transitions.TransitionHandler, WindowContainerTransaction> handler = + mPlayer.dispatchRequest(transition, request, /* skip= */ this); + if (handler == null) { + return null; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a desktop request, so" + + " treat it as Mixed. handler=%s", handler.first); + final MixedTransition mixed = createDefaultMixedTransition( + MixedTransition.TYPE_OPEN_IN_DESKTOP, transition); + mixed.mLeftoversHandler = handler.first; + mActiveTransitions.add(mixed); + return handler.second; } return null; } @@ -341,7 +361,7 @@ public class DefaultMixedHandler implements MixedTransitionHandler, private DefaultMixedTransition createDefaultMixedTransition(int type, IBinder transition) { return new DefaultMixedTransition( type, transition, mPlayer, this, mPipHandler, mSplitHandler, mKeyguardHandler, - mUnfoldHandler, mActivityEmbeddingController); + mUnfoldHandler, mActivityEmbeddingController, mDesktopTasksController); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java index c8921d256d7f..3d3de88cdafc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java @@ -30,6 +30,7 @@ import android.window.TransitionInfo; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -39,15 +40,19 @@ import com.android.wm.shell.unfold.UnfoldTransitionHandler; class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { private final UnfoldTransitionHandler mUnfoldHandler; private final ActivityEmbeddingController mActivityEmbeddingController; + @Nullable + private final DesktopTasksController mDesktopTasksController; DefaultMixedTransition(int type, IBinder transition, Transitions player, MixedTransitionHandler mixedHandler, PipTransitionController pipHandler, StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler, UnfoldTransitionHandler unfoldHandler, - ActivityEmbeddingController activityEmbeddingController) { + ActivityEmbeddingController activityEmbeddingController, + @Nullable DesktopTasksController desktopTasksController) { super(type, transition, player, mixedHandler, pipHandler, splitHandler, keyguardHandler); mUnfoldHandler = unfoldHandler; mActivityEmbeddingController = activityEmbeddingController; + mDesktopTasksController = desktopTasksController; switch (type) { case TYPE_UNFOLD: @@ -57,7 +62,8 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING: case TYPE_ENTER_PIP_FROM_SPLIT: case TYPE_KEYGUARD: - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: + case TYPE_OPEN_IN_DESKTOP: default: break; } @@ -85,11 +91,14 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD -> animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback, mKeyguardHandler, mPipHandler); - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE -> - animateOpenIntentWithRemoteAndPip(transition, info, startTransaction, + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE -> + animateOpenIntentWithRemoteAndPipOrDesktop(transition, info, startTransaction, finishTransaction, finishCallback); case TYPE_UNFOLD -> animateUnfold(info, startTransaction, finishTransaction, finishCallback); + case TYPE_OPEN_IN_DESKTOP -> + animateOpenInDesktop( + transition, info, startTransaction, finishTransaction, finishCallback); default -> throw new IllegalStateException( "Starting default mixed animation with unknown or illegal type: " + mType); }; @@ -146,31 +155,34 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { return true; } - private boolean animateOpenIntentWithRemoteAndPip( + private boolean animateOpenIntentWithRemoteAndPipOrDesktop( @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for opening an intent" - + " with a remote transition and PIP #%d", info.getDebugId()); - boolean handledToPip = tryAnimateOpenIntentWithRemoteAndPip( + + " with a remote transition and PIP or Desktop #%d", info.getDebugId()); + boolean handledToPipOrDesktop = tryAnimateOpenIntentWithRemoteAndPipOrDesktop( info, startTransaction, finishTransaction, finishCallback); // Consume the transition on remote handler if the leftover handler already handle this // transition. And if it cannot, the transition will be handled by remote handler, so don't // consume here. - // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip - if (handledToPip && mHasRequestToRemote + // Need to check leftOverHandler as it may change in + // #animateOpenIntentWithRemoteAndPipOrDesktop + if (handledToPipOrDesktop && mHasRequestToRemote && mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) { mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null); } - return handledToPip; + return handledToPipOrDesktop; } - private boolean tryAnimateOpenIntentWithRemoteAndPip( + private boolean tryAnimateOpenIntentWithRemoteAndPipOrDesktop( @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "tryAnimateOpenIntentWithRemoteAndPipOrDesktop"); TransitionInfo.Change pipChange = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { TransitionInfo.Change change = info.getChanges().get(i); @@ -183,13 +195,31 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { info.getChanges().remove(i); } } + TransitionInfo.Change desktopChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mDesktopTasksController != null + && mDesktopTasksController.isDesktopChange(mTransition, change)) { + if (desktopChange != null) { + throw new IllegalStateException("More than 1 desktop changes in one" + + " transition? " + info); + } + desktopChange = change; + info.getChanges().remove(i); + } + } Transitions.TransitionFinishCallback finishCB = (wct) -> { --mInFlightSubAnimations; joinFinishArgs(wct); if (mInFlightSubAnimations > 0) return; finishCallback.onTransitionFinished(mFinishWCT); }; - if (pipChange == null) { + if ((pipChange == null && desktopChange == null) + || (pipChange != null && desktopChange != null)) { + // Don't split the transition. Let the leftovers handler handle it all. + // TODO: b/? - split the transition into three pieces when there's both a PIP and a + // desktop change are present. For example, during remote intent open over a desktop + // with both a PIP capable task and an immersive task. if (mLeftoversHandler != null) { mInFlightSubAnimations = 1; if (mLeftoversHandler.startAnimation( @@ -198,27 +228,52 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { } } return false; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting PIP into a separate" - + " animation because remote-animation likely doesn't support it #%d", - info.getDebugId()); - // Split the transition into 2 parts: the pip part and the rest. - mInFlightSubAnimations = 2; - // make a new startTransaction because pip's startEnterAnimation "consumes" it so - // we need a separate one to send over to launcher. - SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + } else if (pipChange != null && desktopChange == null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting PIP into a separate" + + " animation because remote-animation likely doesn't support it #%d", + info.getDebugId()); + // Split the transition into 2 parts: the pip part and the rest. + mInFlightSubAnimations = 2; + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + + mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, + mMixedHandler); + return true; + } else if (pipChange == null && desktopChange != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting desktop change into a" + + "separate animation because remote-animation likely doesn't support" + + "it #%d", info.getDebugId()); + mInFlightSubAnimations = 2; + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); - mPipHandler.startEnterAnimation(pipChange, otherStartT, finishTransaction, finishCB); + mDesktopTasksController.animateDesktopChange( + mTransition, desktopChange, otherStartT, finishTransaction, finishCB); - // Dispatch the rest of the transition normally. - if (mLeftoversHandler != null - && mLeftoversHandler.startAnimation(mTransition, info, - startTransaction, finishTransaction, finishCB)) { + // Dispatch the rest of the transition normally. + if (mLeftoversHandler != null + && mLeftoversHandler.startAnimation(mTransition, info, + startTransaction, finishTransaction, finishCB)) { + return true; + } + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, + mMixedHandler); return true; + } else { + throw new IllegalStateException( + "All PIP and Immersive combinations should've been handled"); } - mLeftoversHandler = mPlayer.dispatchTransition( - mTransition, info, startTransaction, finishTransaction, finishCB, mMixedHandler); - return true; } private boolean animateUnfold( @@ -246,6 +301,51 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { mTransition, info, startTransaction, finishTransaction, finishCB); } + private boolean animateOpenInDesktop( + @NonNull IBinder transition, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "animateOpenInDesktop"); + TransitionInfo.Change desktopChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mDesktopTasksController.isDesktopChange(mTransition, change)) { + if (desktopChange != null) { + throw new IllegalStateException("More than 1 desktop changes in one" + + " transition? " + info); + } + desktopChange = change; + info.getChanges().remove(i); + } + } + final Transitions.TransitionFinishCallback finishCB = (wct) -> { + --mInFlightSubAnimations; + joinFinishArgs(wct); + if (mInFlightSubAnimations > 0) return; + finishCallback.onTransitionFinished(mFinishWCT); + }; + if (desktopChange == null) { + if (mLeftoversHandler != null) { + mInFlightSubAnimations = 1; + if (mLeftoversHandler.startAnimation( + mTransition, info, startTransaction, finishTransaction, finishCB)) { + return true; + } + } + return false; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting desktop change into a" + + "separate animation #%d", info.getDebugId()); + mInFlightSubAnimations = 2; + mDesktopTasksController.animateDesktopChange( + transition, desktopChange, startTransaction, finishTransaction, finishCB); + mLeftoversHandler = mPlayer.dispatchTransition( + mTransition, info, startTransaction, finishTransaction, finishCB, mMixedHandler); + return true; + } + @Override void mergeAnimation( @NonNull IBinder transition, @NonNull TransitionInfo info, @@ -279,7 +379,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD: mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); return; - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: mPipHandler.end(); if (mLeftoversHandler != null) { mLeftoversHandler.mergeAnimation( @@ -289,6 +389,10 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_UNFOLD: mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); return; + case TYPE_OPEN_IN_DESKTOP: + mDesktopTasksController.mergeAnimation( + transition, info, t, mergeTarget, finishCallback); + return; default: throw new IllegalStateException("Playing a default mixed transition with unknown or" + " illegal type: " + mType); @@ -310,12 +414,14 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { case TYPE_KEYGUARD: mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); break; - case TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE: + case TYPE_OPTIONS_REMOTE_AND_PIP_OR_DESKTOP_CHANGE: mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); break; case TYPE_UNFOLD: mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); break; + case TYPE_OPEN_IN_DESKTOP: + mDesktopTasksController.onTransitionConsumed(transition, aborted, finishT); default: break; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 346f21b86e65..7c9cd0862b69 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -18,6 +18,7 @@ package com.android.wm.shell.transition; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; @@ -58,6 +59,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.Trace; import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; @@ -800,8 +802,17 @@ public class Transitions implements RemoteCallable<Transitions>, track.mReadyTransitions.add(active); for (int i = 0; i < mObservers.size(); ++i) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + mObservers.get(i).getClass().getSimpleName() + "#onTransitionReady: " + + transitTypeToString(info.getType())); + } mObservers.get(i).onTransitionReady( active.mToken, info, active.mStartT, active.mFinishT); + if (useTrace) { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } /* @@ -931,7 +942,7 @@ public class Transitions implements RemoteCallable<Transitions>, onFinish(ready.mToken, null); return; } - playTransition(ready); + playTransitionWithTracing(ready); // Attempt to merge any more queued-up transitions. processReadyQueue(track); return; @@ -1003,6 +1014,18 @@ public class Transitions implements RemoteCallable<Transitions>, processReadyQueue(track); } + private void playTransitionWithTracing(@NonNull ActiveTransition active) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "playTransition: " + transitTypeToString(active.mInfo.getType())); + } + playTransition(active); + if (useTrace) { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + } + private void playTransition(@NonNull ActiveTransition active) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active); final var token = active.mToken; @@ -1022,6 +1045,12 @@ public class Transitions implements RemoteCallable<Transitions>, if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, + active.mHandler.getClass().getSimpleName() + + "#startAnimation animated " + + transitTypeToString(active.mInfo.getType())); + } return; } } @@ -1052,6 +1081,12 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s", mHandlers.get(i)); mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i)); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, + mHandlers.get(i).getClass().getSimpleName() + + "#startAnimation animated " + + transitTypeToString(info.getType())); + } return mHandlers.get(i); } } @@ -1059,6 +1094,26 @@ public class Transitions implements RemoteCallable<Transitions>, "This shouldn't happen, maybe the default handler is broken."); } + private Pair<TransitionHandler, WindowContainerTransaction> dispatchRequestWithTracing( + @NonNull IBinder transition, @NonNull TransitionRequestInfo request, + @Nullable TransitionHandler skip) { + final boolean useTrace = Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER); + if (useTrace) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "dispatchRequest: " + transitTypeToString(request.getType())); + } + Pair<TransitionHandler, WindowContainerTransaction> result = + dispatchRequest(transition, request, skip); + if (useTrace) { + if (result != null) { + Trace.instant(TRACE_TAG_WINDOW_MANAGER, result.first.getClass().getSimpleName() + + "#handleRequest handled " + transitTypeToString(request.getType())); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + return result; + } + /** * Gives every handler (in order) a chance to handle request until one consumes the transition. * @return the WindowContainerTransaction given by the handler which consumed the transition. @@ -1197,12 +1252,11 @@ public class Transitions implements RemoteCallable<Transitions>, mSleepHandler.handleRequest(transitionToken, request); active.mHandler = mSleepHandler; } else { - for (int i = mHandlers.size() - 1; i >= 0; --i) { - wct = mHandlers.get(i).handleRequest(transitionToken, request); - if (wct != null) { - active.mHandler = mHandlers.get(i); - break; - } + Pair<TransitionHandler, WindowContainerTransaction> requestResult = + dispatchRequestWithTracing(transitionToken, request, /* skip= */ null); + if (requestResult != null) { + active.mHandler = requestResult.first; + wct = requestResult.second; } if (request.getDisplayChange() != null) { TransitionRequestInfo.DisplayChange change = request.getDisplayChange(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 509cb85c96cd..fde01eefee17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -274,6 +274,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL closeDragResizeListener(); mDragResizeListener = new DragResizeInputListener( mContext, + mTaskInfo, mHandler, mChoreographer, mDisplay.getDisplayId(), 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 a06b4a2e09d4..a775cbc6c9f3 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 @@ -32,6 +32,7 @@ import static android.view.WindowInsets.Type.statusBars; import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU; import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; +import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; @@ -103,6 +104,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -221,6 +223,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, }; private final TaskPositionerFactory mTaskPositionerFactory; private final FocusTransitionObserver mFocusTransitionObserver; + private final DesktopModeEventLogger mDesktopModeEventLogger; public DesktopModeWindowDecorViewModel( Context context, @@ -248,7 +251,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, AppHandleEducationController appHandleEducationController, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { this( context, shellExecutor, @@ -281,7 +285,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, new TaskPositionerFactory(), - focusTransitionObserver); + focusTransitionObserver, + desktopModeEventLogger); } @VisibleForTesting @@ -317,7 +322,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + DesktopModeEventLogger desktopModeEventLogger) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -378,6 +384,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, }; mTaskPositionerFactory = taskPositionerFactory; mFocusTransitionObserver = focusTransitionObserver; + mDesktopModeEventLogger = desktopModeEventLogger; shellInit.addInitCallback(this::onInit, this); } @@ -547,15 +554,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, >= MANAGE_WINDOWS_MINIMUM_INSTANCES); } - private void onMaximizeOrRestore(int taskId, String source) { + private void onMaximizeOrRestore(int taskId, String source, ResizeTrigger resizeTrigger, + MotionEvent motionEvent) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; } + mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent, + decoration.mTaskInfo, + mDisplayController, /* displayLayoutSize= */ null); mInteractionJankMonitor.begin( decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source); - mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo); + mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger, + motionEvent); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -568,7 +580,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo); } - private void onSnapResize(int taskId, boolean left) { + private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; @@ -579,13 +591,20 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Toast.makeText(mContext, R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show(); } else { + ResizeTrigger resizeTrigger = + left ? ResizeTrigger.SNAP_LEFT_MENU : ResizeTrigger.SNAP_RIGHT_MENU; + mDesktopModeEventLogger.logTaskResizingStarted(resizeTrigger, motionEvent, + decoration.mTaskInfo, + mDisplayController, /* displayLayoutSize= */ null); mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); mDesktopTasksController.snapToHalfScreen( decoration.mTaskInfo, decoration.mTaskSurface, decoration.mTaskInfo.configuration.windowConfiguration.getBounds(), - left ? SnapPosition.LEFT : SnapPosition.RIGHT); + left ? SnapPosition.LEFT : SnapPosition.RIGHT, + resizeTrigger, + motionEvent); } decoration.closeHandleMenu(); @@ -737,6 +756,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private boolean mTouchscreenInUse; private boolean mHasLongClicked; private int mDragPointerId = -1; + private MotionEvent mMotionEvent; private DesktopModeTouchEventListener( RunningTaskInfo taskInfo, @@ -798,7 +818,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } else { // Full immersive is disabled or task doesn't request/support it, so just // toggle between maximize/restore states. - onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button"); + onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button", + ResizeTrigger.MAXIMIZE_BUTTON, mMotionEvent); } } else if (id == R.id.minimize_window) { mDesktopTasksController.minimizeTask(decoration.mTaskInfo); @@ -807,6 +828,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, @Override public boolean onTouch(View v, MotionEvent e) { + mMotionEvent = e; final int id = v.getId(); final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) { @@ -897,6 +919,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, */ @Override public boolean onGenericMotion(View v, MotionEvent ev) { + mMotionEvent = ev; final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); if (ev.getAction() == ACTION_HOVER_ENTER && id == R.id.maximize_window) { @@ -1040,7 +1063,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, taskInfo, decoration.mTaskSurface, position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), newTaskBounds, decoration.calculateValidDragArea(), - new Rect(mOnDragStartInitialBounds)); + new Rect(mOnDragStartInitialBounds), e); if (touchingButton && !mHasLongClicked) { // We need the input event to not be consumed here to end the ripple // effect on the touched button. We will reset drag state in the ensuing @@ -1087,7 +1110,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // Disallow double-tap to resize when in full immersive. return false; } - onMaximizeOrRestore(mTaskId, "double_tap"); + onMaximizeOrRestore(mTaskId, "double_tap", ResizeTrigger.DOUBLE_TAP_APP_HEADER, e); return true; } } @@ -1484,7 +1507,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mGenericLinksParser, mAssistContentRequester, mMultiInstanceHelper, - mWindowDecorCaptionHandleRepository); + mWindowDecorCaptionHandleRepository, + mDesktopModeEventLogger); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); final TaskPositioner taskPositioner = mTaskPositionerFactory.create( @@ -1501,15 +1525,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, taskPositioner); windowDecoration.setOnMaximizeOrRestoreClickListener(() -> { - onMaximizeOrRestore(taskInfo.taskId, "maximize_menu"); + onMaximizeOrRestore(taskInfo.taskId, "maximize_menu", ResizeTrigger.MAXIMIZE_MENU, + touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnLeftSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, true /* isLeft */); + onSnapResize(taskInfo.taskId, /* isLeft= */ true, touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnRightSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, false /* isLeft */); + onSnapResize(taskInfo.taskId, /* isLeft= */ false, touchEventListener.mMotionEvent); return Unit.INSTANCE; }); windowDecoration.setOnToDesktopClickListener(desktopModeTransitionSource -> { 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 6eb20b9e3ae5..d94f3a9a70c6 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 @@ -94,6 +94,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; @@ -216,7 +217,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { this (context, userContext, displayController, splitScreenController, desktopRepository, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser, @@ -227,7 +229,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper, - windowDecorCaptionHandleRepository); + windowDecorCaptionHandleRepository, desktopModeEventLogger); } DesktopModeWindowDecoration( @@ -256,11 +258,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin MaximizeMenuFactory maximizeMenuFactory, HandleMenuFactory handleMenuFactory, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory); + surfaceControlViewHostFactory, desktopModeEventLogger); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; @@ -605,6 +608,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Trace.beginSection("DesktopModeWindowDecoration#relayout-DragResizeInputListener"); mDragResizeListener = new DragResizeInputListener( mContext, + mTaskInfo, mHandler, mChoreographer, mDisplay.getDisplayId(), @@ -612,7 +616,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mDragPositioningCallback, mSurfaceControlBuilderSupplier, mSurfaceControlTransactionSupplier, - mDisplayController); + mDisplayController, + mDesktopModeEventLogger); Trace.endSection(); } @@ -1700,7 +1705,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, - WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository) { + WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, + DesktopModeEventLogger desktopModeEventLogger) { return new DesktopModeWindowDecoration( context, userContext, @@ -1719,7 +1725,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin genericLinksParser, assistContentRequester, multiInstanceHelper, - windowDecorCaptionHandleRepository); + windowDecorCaptionHandleRepository, + desktopModeEventLogger); } } 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 60c922293d80..78e7962dcec3 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 @@ -29,8 +29,6 @@ import android.util.DisplayMetrics; import android.view.SurfaceControl; import android.window.DesktopModeFlags; -import androidx.annotation.NonNull; - import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -129,13 +127,15 @@ public class DragPositioningCallbackUtility { // If width or height are negative or exceeding the width or height constraints, revert the // respective bounds to use previous bound dimensions. - if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController, + if (isExceedingWidthConstraint(repositionTaskBounds.width(), + /* startingWidth= */ oldRight - oldLeft, stableBounds, displayController, windowDecoration)) { repositionTaskBounds.right = oldRight; repositionTaskBounds.left = oldLeft; isAspectRatioMaintained = false; } - if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController, + if (isExceedingHeightConstraint(repositionTaskBounds.height(), + /* startingHeight= */oldBottom - oldTop, stableBounds, displayController, windowDecoration)) { repositionTaskBounds.top = oldTop; repositionTaskBounds.bottom = oldBottom; @@ -208,28 +208,34 @@ public class DragPositioningCallbackUtility { return result; } - private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds, + private static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { + boolean isSizeIncreasing = (repositionedWidth - startingWidth) > 0; // Check if width is less than the minimum width constraint. - if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) { - return true; + if (repositionedWidth < getMinWidth(displayController, windowDecoration)) { + // Only allow width to be increased if it is already below minimum. + return !isSizeIncreasing; } // Check if width is more than the maximum resize bounds on desktop windowing mode. + // Only allow width to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) - && repositionTaskBounds.width() > maxResizeBounds.width(); + && repositionedWidth > maxResizeBounds.width() && isSizeIncreasing; } - private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds, + private static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { + boolean isSizeIncreasing = (repositionedHeight - startingHeight) > 0; // Check if height is less than the minimum height constraint. - if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) { - return true; + if (repositionedHeight < getMinHeight(displayController, windowDecoration)) { + // Only allow height to be increased if it is already below minimum. + return !isSizeIncreasing; } // Check if height is more than the maximum resize bounds on desktop windowing mode. + // Only allow height to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) - && repositionTaskBounds.height() > maxResizeBounds.height(); + && repositionedHeight > maxResizeBounds.height() && isSizeIncreasing; } private static float getMinWidth(DisplayController displayController, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 4ff394e2b1a9..420409705b05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -29,10 +29,12 @@ import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT; import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT; import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP; +import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen; import android.annotation.NonNull; +import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -59,6 +61,7 @@ import android.window.InputTransferToken; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -83,14 +86,17 @@ class DragResizeInputListener implements AutoCloseable { private final TaskResizeInputEventReceiver mInputEventReceiver; private final Context mContext; + private final RunningTaskInfo mTaskInfo; private final SurfaceControl mInputSinkSurface; private final IBinder mSinkClientToken; private final InputChannel mSinkInputChannel; private final DisplayController mDisplayController; + private final DesktopModeEventLogger mDesktopModeEventLogger; private final Region mTouchRegion = new Region(); DragResizeInputListener( Context context, + RunningTaskInfo taskInfo, Handler handler, Choreographer choreographer, int displayId, @@ -98,12 +104,15 @@ class DragResizeInputListener implements AutoCloseable { DragPositioningCallback callback, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, - DisplayController displayController) { + DisplayController displayController, + DesktopModeEventLogger desktopModeEventLogger) { mContext = context; + mTaskInfo = taskInfo; mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mDisplayId = displayId; mDecorationSurface = decorationSurface; mDisplayController = displayController; + mDesktopModeEventLogger = desktopModeEventLogger; mClientToken = new Binder(); final InputTransferToken inputTransferToken = new InputTransferToken(); mInputChannel = new InputChannel(); @@ -125,11 +134,12 @@ class DragResizeInputListener implements AutoCloseable { e.rethrowFromSystemServer(); } - mInputEventReceiver = new TaskResizeInputEventReceiver(context, mInputChannel, callback, + mInputEventReceiver = new TaskResizeInputEventReceiver(context, mTaskInfo, mInputChannel, + callback, handler, choreographer, () -> { final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId); return new Size(layout.width(), layout.height()); - }, this::updateSinkInputChannel); + }, this::updateSinkInputChannel, mDesktopModeEventLogger); mInputEventReceiver.setTouchSlop(ViewConfiguration.get(context).getScaledTouchSlop()); mInputSinkSurface = surfaceControlBuilderSupplier.get() @@ -163,6 +173,22 @@ class DragResizeInputListener implements AutoCloseable { } } + DragResizeInputListener( + Context context, + RunningTaskInfo taskInfo, + Handler handler, + Choreographer choreographer, + int displayId, + SurfaceControl decorationSurface, + DragPositioningCallback callback, + Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, + Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, + DisplayController displayController) { + this(context, taskInfo, handler, choreographer, displayId, decorationSurface, callback, + surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, displayController, + new DesktopModeEventLogger()); + } + /** * Updates the geometry (the touch region) of this drag resize handler. * @@ -274,6 +300,7 @@ class DragResizeInputListener implements AutoCloseable { private static class TaskResizeInputEventReceiver extends InputEventReceiver implements DragDetector.MotionEventHandler { @NonNull private final Context mContext; + @NonNull private final RunningTaskInfo mTaskInfo; private final InputManager mInputManager; @NonNull private final InputChannel mInputChannel; @NonNull private final DragPositioningCallback mCallback; @@ -282,6 +309,7 @@ class DragResizeInputListener implements AutoCloseable { @NonNull private final DragDetector mDragDetector; @NonNull private final Supplier<Size> mDisplayLayoutSizeSupplier; @NonNull private final Consumer<Region> mTouchRegionConsumer; + @NonNull private final DesktopModeEventLogger mDesktopModeEventLogger; private final Rect mTmpRect = new Rect(); private boolean mConsumeBatchEventScheduled; private DragResizeWindowGeometry mDragResizeWindowGeometry; @@ -293,15 +321,24 @@ class DragResizeInputListener implements AutoCloseable { // resize events. For example, if multiple fingers are touching the screen, then each one // has a separate pointer id, but we only accept drag input from one. private int mDragPointerId = -1; + // The type of resizing that is currently being done. Used to track the same resize trigger + // on start and end of the resizing action. + private ResizeTrigger mResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER; + // The last MotionEvent on ACTION_DOWN, used to track the input tool type and source for + // logging the start and end of the resizing action. + private MotionEvent mLastMotionEventOnDown; private TaskResizeInputEventReceiver(@NonNull Context context, + @NonNull RunningTaskInfo taskInfo, @NonNull InputChannel inputChannel, @NonNull DragPositioningCallback callback, @NonNull Handler handler, @NonNull Choreographer choreographer, @NonNull Supplier<Size> displayLayoutSizeSupplier, - @NonNull Consumer<Region> touchRegionConsumer) { + @NonNull Consumer<Region> touchRegionConsumer, + @NonNull DesktopModeEventLogger desktopModeEventLogger) { super(inputChannel, handler.getLooper()); mContext = context; + mTaskInfo = taskInfo; mInputManager = context.getSystemService(InputManager.class); mInputChannel = inputChannel; mCallback = callback; @@ -322,6 +359,7 @@ class DragResizeInputListener implements AutoCloseable { ViewConfiguration.get(mContext).getScaledTouchSlop()); mDisplayLayoutSizeSupplier = displayLayoutSizeSupplier; mTouchRegionConsumer = touchRegionConsumer; + mDesktopModeEventLogger = desktopModeEventLogger; } /** @@ -395,6 +433,7 @@ class DragResizeInputListener implements AutoCloseable { @Override public boolean handleMotionEvent(View v, MotionEvent e) { boolean result = false; + // Check if this is a touch event vs mouse event. // Touch events are tracked in four corners. Other events are tracked in resize edges. switch (e.getActionMasked()) { @@ -416,6 +455,13 @@ class DragResizeInputListener implements AutoCloseable { "%s: Handling action down, update ctrlType to %d", TAG, ctrlType); mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType, rawX, rawY); + mLastMotionEventOnDown = e; + mResizeTrigger = (ctrlType == CTRL_TYPE_BOTTOM || ctrlType == CTRL_TYPE_TOP + || ctrlType == CTRL_TYPE_RIGHT || ctrlType == CTRL_TYPE_LEFT) + ? ResizeTrigger.EDGE : ResizeTrigger.CORNER; + mDesktopModeEventLogger.logTaskResizingStarted(mResizeTrigger, + e, mTaskInfo, /* displayController= */ null, + /* displayLayoutSize= */ mDisplayLayoutSizeSupplier.get()); // Increase the input sink region to cover the whole screen; this is to // prevent input and focus from going to other tasks during a drag resize. updateInputSinkRegionForDrag(mDragStartTaskBounds); @@ -464,6 +510,12 @@ class DragResizeInputListener implements AutoCloseable { if (taskBounds.equals(mDragStartTaskBounds)) { mTouchRegionConsumer.accept(mTouchRegion); } + + mDesktopModeEventLogger.logTaskResizingEnded(mResizeTrigger, + mLastMotionEventOnDown, mTaskInfo, taskBounds.height(), + taskBounds.width(), + /* displayController= */ null, + /* displayLayoutSize= */ mDisplayLayoutSizeSupplier.get()); } mShouldHandleEvents = false; mDragPointerId = -1; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java index 33d1c260cb84..844ceb304bde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java @@ -181,7 +181,7 @@ final class DragResizeWindowGeometry { } private boolean isInEdgeResizeBounds(float x, float y) { - return calculateEdgeResizeCtrlType(x, y) != 0; + return calculateEdgeResizeCtrlType(x, y) != CTRL_TYPE_UNDEFINED; } /** 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 6b3b357f2f7b..34cc0986c83f 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 @@ -58,6 +58,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; @@ -111,6 +112,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Context mContext; final @NonNull Context mUserContext; final @NonNull DisplayController mDisplayController; + final @NonNull DesktopModeEventLogger mDesktopModeEventLogger; final ShellTaskOrganizer mTaskOrganizer; final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; @@ -163,7 +165,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> this(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, - new SurfaceControlViewHostFactory() {}); + new SurfaceControlViewHostFactory() {}, new DesktopModeEventLogger()); } WindowDecoration( @@ -177,13 +179,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory, + @NonNull DesktopModeEventLogger desktopModeEventLogger + ) { mContext = context; mUserContext = userContext; mDisplayController = displayController; mTaskOrganizer = taskOrganizer; mTaskInfo = taskInfo; mTaskSurface = cloneSurfaceControl(taskSurface, surfaceControlSupplier); + mDesktopModeEventLogger = desktopModeEventLogger; mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier; mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; mWindowContainerTransactionSupplier = windowContainerTransactionSupplier; diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index b7ddfd1fc6eb..4fe66f3357a3 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -298,12 +298,18 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("MAXIMIZE_APP"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + it.type == TransitionType.DESKTOP_MODE_TOGGLE_RESIZE + } + } + } + ), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf( AppLayerIncreasesInSize(DESKTOP_MODE_APP), @@ -316,12 +322,18 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + it.type == TransitionType.DESKTOP_MODE_TOGGLE_RESIZE + } + } + } + ), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index e6bd05b82be9..f935ac76bbeb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -40,6 +40,8 @@ public final class TestRunningTaskInfoBuilder { private WindowContainerToken mToken = createMockWCToken(); private int mParentTaskId = INVALID_TASK_ID; + private int mUid = INVALID_TASK_ID; + private int mTaskId = INVALID_TASK_ID; private Intent mBaseIntent = new Intent(); private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD; private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -73,6 +75,18 @@ public final class TestRunningTaskInfoBuilder { return this; } + /** Sets the task info's effective UID. */ + public TestRunningTaskInfoBuilder setUid(int uid) { + mUid = uid; + return this; + } + + /** Sets the task info's UID. */ + public TestRunningTaskInfoBuilder setTaskId(int taskId) { + mTaskId = taskId; + return this; + } + /** * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default * an empty intent is assigned @@ -132,7 +146,8 @@ public final class TestRunningTaskInfoBuilder { public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.taskId = sNextTaskId++; + info.taskId = (mTaskId == INVALID_TASK_ID) ? sNextTaskId++ : mTaskId; + info.effectiveUid = mUid; info.baseIntent = mBaseIntent; info.parentTaskId = mParentTaskId; info.displayId = mDisplayId; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index dee0b23a42f5..72d4dc6ffac9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -51,6 +51,7 @@ import android.content.pm.ApplicationInfo; import android.graphics.Point; import android.graphics.Rect; import android.hardware.input.InputManager; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -776,6 +777,14 @@ public class BackAnimationControllerTest extends ShellTestCase { verify(mergeCallback, never()).onTransitionFinished(any()); } + @Test + public void testBackAnimationControllersRecoversFromBadState() throws RemoteException { + // put controller into bad state (initial state but mBackGestureStarted=true) + mController.mBackGestureStarted = true; + verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY, + mDefaultCrossActivityBackAnimation.getRunner()); + } + private RemoteAnimationTarget[] createAppAnimationTargets(int openTaskId, int closeTaskId) { final RemoteAnimationTarget openT = createSingleAnimationTarget(openTaskId, RemoteAnimationTarget.MODE_OPENING); @@ -804,7 +813,10 @@ public class BackAnimationControllerTest extends ShellTestCase { if (taskId != INVALID_TASK_ID) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = taskId; - taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class)); + final IWindowContainerToken mockT = mock(IWindowContainerToken.class); + Binder binder = new Binder(); + doReturn(binder).when(mockT).asBinder(); + taskInfo.token = new WindowContainerToken(mockT); change = new TransitionInfo.Change( taskInfo.token, b.build()); change.setTaskInfo(taskInfo); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java index d6059a88e9c7..8fd7c0ec3099 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIStatusManagerTest.java @@ -16,6 +16,10 @@ package com.android.wm.shell.compatui; +import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUCATION_HIDDEN; + +import static junit.framework.Assert.assertEquals; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -25,7 +29,6 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -63,13 +66,75 @@ public class CompatUIStatusManagerTest extends ShellTestCase { assertFalse(mStatusManager.isEducationVisible()); } + @Test + public void valuesAreCached() { + // At the beginning the value is not read or written because + // we access the reader in lazy way. + mTestState.assertReaderInvocations(0); + mTestState.assertWriterInvocations(0); + + // We read the value when we start. Initial value is hidden. + assertFalse(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(0); + + // We send the event for the same state which is not written. + mStatusManager.onEducationHidden(); + assertFalse(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(0); + + // We send the event for the different state which is written but + // not read again. + mStatusManager.onEducationShown(); + assertTrue(mStatusManager.isEducationVisible()); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(1); + + // We read multiple times and we don't read the value again + mStatusManager.isEducationVisible(); + mStatusManager.isEducationVisible(); + mStatusManager.isEducationVisible(); + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(1); + + // We write different values. Writer is only accessed when + // the value changes. + mStatusManager.onEducationHidden(); // change + mStatusManager.onEducationHidden(); + mStatusManager.onEducationShown(); // change + mStatusManager.onEducationShown(); + mStatusManager.onEducationHidden(); // change + mStatusManager.onEducationShown(); // change + mTestState.assertReaderInvocations(1); + mTestState.assertWriterInvocations(5); + } + static class FakeCompatUIStatusManagerTest { - int mCurrentStatus = 0; + int mCurrentStatus = COMPAT_UI_EDUCATION_HIDDEN; + + int mReaderInvocations; + + int mWriterInvocations; + + final IntSupplier mReader = () -> { + mReaderInvocations++; + return mCurrentStatus; + }; + + final IntConsumer mWriter = newStatus -> { + mWriterInvocations++; + mCurrentStatus = newStatus; + }; - final IntSupplier mReader = () -> mCurrentStatus; + void assertWriterInvocations(int expected) { + assertEquals(expected, mWriterInvocations); + } - final IntConsumer mWriter = newStatus -> mCurrentStatus = newStatus; + void assertReaderInvocations(int expected) { + assertEquals(expected, mReaderInvocations); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt index ef99b000d759..e83f5c7a79a1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS import android.graphics.Rect import android.os.Binder @@ -58,13 +59,13 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever /** - * Tests for [DesktopFullImmersiveTransitionHandler]. + * Tests for [DesktopImmersiveController]. * - * Usage: atest WMShellUnitTests:DesktopFullImmersiveTransitionHandlerTest + * Usage: atest WMShellUnitTests:DesktopImmersiveControllerTest */ @SmallTest @RunWith(AndroidTestingRunner::class) -class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { +class DesktopImmersiveControllerTest : ShellTestCase() { @JvmField @Rule val setFlagsRule = SetFlagsRule() @@ -75,7 +76,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Mock private lateinit var mockDisplayLayout: DisplayLayout private val transactionSupplier = { SurfaceControl.Transaction() } - private lateinit var immersiveHandler: DesktopFullImmersiveTransitionHandler + private lateinit var controller: DesktopImmersiveController @Before fun setUp() { @@ -87,7 +88,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { invocation -> (invocation.getArgument(0) as Rect).set(STABLE_BOUNDS) } - immersiveHandler = DesktopFullImmersiveTransitionHandler( + controller = DesktopImmersiveController( transitions = mockTransitions, desktopRepository = desktopRepository, displayController = mockDisplayController, @@ -100,7 +101,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_transitionReady_updatesRepository() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -108,16 +109,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isTrue() @@ -128,7 +127,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_savesPreImmersiveBounds() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -137,16 +136,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNull() - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeFullImmersive(task.taskId)).isNotNull() @@ -156,7 +153,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun exitImmersive_transitionReady_updatesRepository() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -164,16 +161,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToNonImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -184,7 +179,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun exitImmersive_onTransitionReady_removesBoundsBeforeImmersive() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) desktopRepository.setTaskInFullImmersiveState( displayId = task.displayId, @@ -193,16 +188,14 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { ) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.onTransitionReady( + controller.moveTaskToNonImmersive(task) + controller.onTransitionReady( transition = mockBinder, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() @@ -217,16 +210,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = mock(IBinder::class.java), info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { - taskInfo = task - setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90) - } - ) - ) + changes = listOf(createChange(task).apply { + setRotation(/* start= */ Surface.ROTATION_0, /* end= */ Surface.ROTATION_90) + }) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -236,28 +228,28 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { fun enterImmersive_inProgress_ignores() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) - immersiveHandler.moveTaskToImmersive(task) - immersiveHandler.moveTaskToImmersive(task) + controller.moveTaskToImmersive(task) + controller.moveTaskToImmersive(task) verify(mockTransitions, times(1)) - .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)) + .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)) } @Test fun exitImmersive_inProgress_ignores() { val task = createFreeformTask() val mockBinder = mock(IBinder::class.java) - whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler))) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) .thenReturn(mockBinder) - immersiveHandler.moveTaskToNonImmersive(task) - immersiveHandler.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) verify(mockTransitions, times(1)) - .startTransition(eq(TRANSIT_CHANGE), any(), eq(immersiveHandler)) + .startTransition(eq(TRANSIT_CHANGE), any(), eq(controller)) } @Test @@ -273,9 +265,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isTrue() @@ -294,9 +286,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -315,7 +307,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) assertThat(wct.hasBoundsChange(task.token)).isTrue() } @@ -333,13 +325,38 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) assertThat(wct.hasBoundsChange(task.token)).isFalse() } @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersiveIfApplicable_byDisplay_withExcludeTask_doesNotExit() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + controller.exitImmersiveIfApplicable( + wct = wct, + displayId = DEFAULT_DISPLAY, + excludeTaskId = task.taskId + )?.invoke(transition) + + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == transition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun exitImmersiveIfApplicable_byTask_inImmersive_changesTaskBounds() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) @@ -350,7 +367,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat(wct.hasBoundsChange(task.token)).isTrue() } @@ -367,7 +384,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task) + controller.exitImmersiveIfApplicable(wct, task) assertThat(wct.hasBoundsChange(task.token)).isFalse() } @@ -385,9 +402,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isTrue() @@ -406,9 +423,9 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = false ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(transition) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(transition) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -416,7 +433,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun onTransitionReady_pendingExit_removesPendingExit() { + fun onTransitionReady_pendingExit_removesPendingExitOnFinish() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) val wct = WindowContainerTransaction() @@ -426,18 +443,19 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) + controller.onTransitionFinished(transition, aborted = false) - assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit -> + assertThat(controller.pendingExternalExitTransitions.any { exit -> exit.transition == transition && exit.displayId == DEFAULT_DISPLAY && exit.taskId == task.taskId }).isFalse() @@ -445,6 +463,42 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun onTransitionReady_pendingExit_withMerge_removesPendingExitOnFinish() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + val mergedToTransition = Binder() + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + + controller.onTransitionReady( + transition = transition, + info = createTransitionInfo( + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), + ) + controller.onTransitionMerged(transition, mergedToTransition) + controller.onTransitionFinished(mergedToTransition, aborted = false) + + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == transition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + assertThat(controller.pendingExternalExitTransitions.any { exit -> + exit.transition == mergedToTransition && exit.displayId == DEFAULT_DISPLAY + && exit.taskId == task.taskId + }).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun onTransitionReady_pendingExit_updatesRepository() { val task = createFreeformTask() whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) @@ -455,15 +509,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.isTaskInFullImmersiveState(task.taskId)).isFalse() @@ -485,15 +539,15 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, Rect(100, 100, 600, 600)) - immersiveHandler.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) - immersiveHandler.onTransitionReady( + controller.onTransitionReady( transition = transition, info = createTransitionInfo( - changes = listOf( - TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task } - ) - ) + changes = listOf(createChange(task)) + ), + startTransaction = SurfaceControl.Transaction(), + finishTransaction = SurfaceControl.Transaction(), ) assertThat(desktopRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() @@ -512,7 +566,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, calculateMaximizeBounds(mockDisplayLayout, task)) @@ -536,7 +590,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { val preImmersiveBounds = Rect(100, 100, 500, 500) desktopRepository.saveBoundsBeforeFullImmersive(task.taskId, preImmersiveBounds) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, preImmersiveBounds) @@ -559,7 +613,7 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct = wct, taskInfo = task) + controller.exitImmersiveIfApplicable(wct = wct, taskInfo = task) assertThat( wct.hasBoundsChange(task.token, calculateInitialBounds(mockDisplayLayout, task)) @@ -577,13 +631,32 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { taskId = task.taskId, immersive = true ) - immersiveHandler.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) + controller.exitImmersiveIfApplicable(wct, task)?.invoke(Binder()) - immersiveHandler.moveTaskToNonImmersive(task) + controller.moveTaskToNonImmersive(task) verify(mockTransitions, never()).startTransition(any(), any(), any()) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun exitImmersiveIfApplicable_inImmersive_isImmersiveChange() { + val task = createFreeformTask() + whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + val wct = WindowContainerTransaction() + val transition = Binder() + val change = createChange(task) + desktopRepository.setTaskInFullImmersiveState( + displayId = DEFAULT_DISPLAY, + taskId = task.taskId, + immersive = true + ) + + controller.exitImmersiveIfApplicable(transition, wct, DEFAULT_DISPLAY) + + assertThat(controller.isImmersiveChange(transition, change)).isTrue() + } + private fun createTransitionInfo( @TransitionType type: Int = TRANSIT_CHANGE, @TransitionFlags flags: Int = 0, @@ -592,6 +665,11 @@ class DesktopFullImmersiveTransitionHandlerTest : ShellTestCase() { changes.forEach { change -> addChange(change) } } + private fun createChange(task: RunningTaskInfo): TransitionInfo.Change = + TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + } + private fun WindowContainerTransaction.hasBoundsChange(token: WindowContainerToken): Boolean = this.changes.any { change -> change.key == token.asBinder() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt index 81d59d586dd3..be0663cbd70d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt @@ -24,6 +24,9 @@ import android.app.WindowConfiguration.WindowingMode import android.os.Binder import android.os.Handler import android.os.IBinder +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl @@ -33,6 +36,7 @@ import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE import com.android.internal.jank.InteractionJankMonitor +import com.android.window.flags.Flags import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.freeform.FreeformTaskTransitionHandler @@ -41,6 +45,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock @@ -60,6 +65,8 @@ import org.mockito.kotlin.whenever @RunWith(AndroidTestingRunner::class) class DesktopMixedTransitionHandlerTest : ShellTestCase() { + @JvmField @Rule val setFlagsRule = SetFlagsRule() + @Mock lateinit var transitions: Transitions @Mock lateinit var desktopRepository: DesktopRepository @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler @@ -106,6 +113,19 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { } @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) + fun startRemoveTransition_callsFreeformTaskTransitionHandler() { + val wct = WindowContainerTransaction() + whenever(freeformTaskTransitionHandler.startRemoveTransition(wct)) + .thenReturn(mock()) + + mixedHandler.startRemoveTransition(wct) + + verify(freeformTaskTransitionHandler).startRemoveTransition(wct) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS) fun startRemoveTransition_startsCloseTransition() { val wct = WindowContainerTransaction() whenever(transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, mixedHandler)) @@ -147,7 +167,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { fun startAnimation_withClosingDesktopTask_callsCloseTaskHandler() { val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) - whenever(desktopRepository.getActiveNonMinimizedTaskCount(any())).thenReturn(2) + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2) whenever( closeDesktopTaskTransitionHandler.startAnimation(any(), any(), any(), any(), any()) ) @@ -170,7 +190,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { fun startAnimation_withClosingLastDesktopTask_dispatchesTransition() { val transition = mock<IBinder>() val transitionInfo = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)) - whenever(desktopRepository.getActiveNonMinimizedTaskCount(any())).thenReturn(1) + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(1) whenever(transitions.dispatchTransition(any(), any(), any(), any(), any(), any())) .thenReturn(mock()) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index 0825b6b0d7be..2a82e6e4f7b8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -16,9 +16,12 @@ package com.android.wm.shell.desktopmode +import android.app.ActivityManager.RunningTaskInfo +import android.graphics.Rect import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker import com.android.dx.mockito.inline.extended.ExtendedMockito.verify import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions @@ -27,6 +30,9 @@ import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod @@ -39,9 +45,13 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_M import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason import com.google.common.truth.Truth.assertThat +import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever /** * Tests for [DesktopModeEventLogger]. @@ -49,6 +59,8 @@ import org.mockito.kotlin.eq class DesktopModeEventLoggerTest : ShellTestCase() { private val desktopModeEventLogger = DesktopModeEventLogger() + val displayController = mock<DisplayController>() + val displayLayout = mock<DisplayLayout>() @JvmField @Rule(order = 0) @@ -60,6 +72,13 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Rule(order = 1) val setFlagsRule = SetFlagsRule() + @Before + fun setUp() { + doReturn(displayLayout).whenever(displayController).getDisplayLayout(anyInt()) + doReturn(DISPLAY_WIDTH).whenever(displayLayout).width() + doReturn(DISPLAY_HEIGHT).whenever(displayLayout).height() + } + @Test fun logSessionEnter_logsEnterReasonWithNewSessionId() { desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) @@ -467,7 +486,8 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Test fun logTaskResizingStarted_noOngoingSession_doesNotLog() { - desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER, + null, createTaskInfo()) verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) @@ -478,13 +498,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() { val sessionId = startDesktopModeSession() - desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingStarted(ResizeTrigger.CORNER, + null, createTaskInfo(), displayController) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ - eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER), /* resizing_stage */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE), /* input_method */ @@ -500,7 +521,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_width */ eq(TASK_SIZE_UPDATE.taskWidth), /* display_area */ - eq(TASK_SIZE_UPDATE.displayArea), + eq(DISPLAY_AREA), ) } verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) @@ -508,7 +529,8 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @Test fun logTaskResizingEnded_noOngoingSession_doesNotLog() { - desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER, + null, createTaskInfo()) verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) @@ -519,13 +541,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() { val sessionId = startDesktopModeSession() - desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + desktopModeEventLogger.logTaskResizingEnded(ResizeTrigger.CORNER, + null, createTaskInfo(), displayController = displayController) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ - eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__CORNER_RESIZE_TRIGGER), /* resizing_stage */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE), /* input_method */ @@ -541,7 +564,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_width */ eq(TASK_SIZE_UPDATE.taskWidth), /* display_area */ - eq(TASK_SIZE_UPDATE.displayArea), + eq(DISPLAY_AREA), ) } verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) @@ -585,8 +608,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { } } + private fun createTaskInfo(): RunningTaskInfo { + return TestRunningTaskInfoBuilder().setTaskId(TASK_ID) + .setUid(TASK_UID) + .setBounds(Rect(TASK_X, TASK_Y, TASK_WIDTH, TASK_HEIGHT)) + .build() + } + private companion object { - private const val sessionId = 1 private const val TASK_ID = 1 private const val TASK_UID = 1 private const val TASK_X = 0 @@ -594,7 +623,9 @@ class DesktopModeEventLoggerTest : ShellTestCase() { private const val TASK_HEIGHT = 100 private const val TASK_WIDTH = 100 private const val TASK_COUNT = 1 - private const val DISPLAY_AREA = 1000 + private const val DISPLAY_WIDTH = 500 + private const val DISPLAY_HEIGHT = 500 + private const val DISPLAY_AREA = DISPLAY_HEIGHT * DISPLAY_WIDTH private val TASK_UPDATE = TaskUpdate( TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index 3e2280393c2b..d90443c99d37 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -898,7 +898,7 @@ class DesktopRepositoryTest : ShellTestCase() { } @Test - fun getActiveNonMinimizedOrderedTasks_returnsFreeformTasksInCorrectOrder() { + fun getExpandedTasksOrdered_returnsFreeformTasksInCorrectOrder() { repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3) @@ -907,13 +907,13 @@ class DesktopRepositoryTest : ShellTestCase() { repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 2) repo.addOrMoveFreeformTaskToTop(displayId = 0, taskId = 1) - val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = 0) + val tasks = repo.getExpandedTasksOrdered(displayId = 0) assertThat(tasks).containsExactly(1, 2, 3).inOrder() } @Test - fun getActiveNonMinimizedOrderedTasks_excludesMinimizedTasks() { + fun getExpandedTasksOrdered_excludesMinimizedTasks() { repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2) repo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 3) @@ -923,7 +923,7 @@ class DesktopRepositoryTest : ShellTestCase() { repo.addOrMoveFreeformTaskToTop(displayId = DEFAULT_DISPLAY, taskId = 1) repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2) - val tasks = repo.getActiveNonMinimizedOrderedTasks(displayId = DEFAULT_DISPLAY) + val tasks = repo.getExpandedTasksOrdered(displayId = DEFAULT_DISPLAY) assertThat(tasks).containsExactly(1, 3).inOrder() } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 55b44ac935ea..bc2b36ccd835 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -54,6 +54,7 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.DragEvent import android.view.Gravity import android.view.KeyEvent +import android.view.MotionEvent import android.view.SurfaceControl import android.view.WindowInsets import android.view.WindowManager @@ -99,6 +100,7 @@ import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask @@ -199,7 +201,7 @@ class DesktopTasksControllerTest : ShellTestCase() { lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler @Mock - lateinit var mockDesktopFullImmersiveTransitionHandler: DesktopFullImmersiveTransitionHandler + lateinit var mMockDesktopImmersiveController: DesktopImmersiveController @Mock lateinit var launchAdjacentController: LaunchAdjacentController @Mock lateinit var splitScreenController: SplitScreenController @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler @@ -214,9 +216,11 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter @Mock private lateinit var mockHandler: Handler + @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger @Mock lateinit var persistentRepository: DesktopPersistentRepository @Mock private lateinit var mockInputManager: InputManager @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver + @Mock lateinit var motionEvent: MotionEvent private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController @@ -295,6 +299,8 @@ class DesktopTasksControllerTest : ShellTestCase() { recentsTransitionStateListener = captor.value controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener + + assumeTrue(ENABLE_SHELL_TRANSITIONS) } private fun createController(): DesktopTasksController { @@ -316,7 +322,7 @@ class DesktopTasksControllerTest : ShellTestCase() { dragAndDropTransitionHandler, toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler, - mockDesktopFullImmersiveTransitionHandler, + mMockDesktopImmersiveController, taskRepository, desktopModeLoggerTransitionObserver, launchAdjacentController, @@ -329,6 +335,7 @@ class DesktopTasksControllerTest : ShellTestCase() { mockHandler, mockInputManager, mockFocusTransitionObserver, + desktopModeEventLogger, ) } @@ -357,9 +364,17 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask() val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1) - verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task1, + STABLE_BOUNDS.height(), + STABLE_BOUNDS.width(), + displayController + ) assertThat(argumentCaptor.value).isTrue() } @@ -376,9 +391,17 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask(bounds = stableBounds, active = true) val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1) - verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task1, + 0, + 0, + displayController + ) assertThat(argumentCaptor.value).isFalse() } @@ -754,7 +777,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) fun handleRequest_newFreeformTaskLaunch_cascadeApplied() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) setUpLandscapeDisplay() val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) @@ -773,7 +795,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS) fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) setUpLandscapeDisplay() val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) @@ -1752,7 +1773,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.minimizeTask(task) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(any(), eq(task)) + verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task)) } @Test @@ -1762,7 +1783,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val runOnTransit = RunOnStartTransitionCallback() whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - whenever(mockDesktopFullImmersiveTransitionHandler.exitImmersiveIfApplicable(any(), eq(task))) + whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task))) .thenReturn(runOnTransit) controller.minimizeTask(task) @@ -1773,8 +1794,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) @@ -1791,8 +1810,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) @@ -1818,8 +1835,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskToFreeform_underTaskLimit_dontMinimize() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskVisible(freeformTask) val fullscreenTask = createFullscreenTask() @@ -1833,8 +1848,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskToFreeform_bringsTasksOverLimit_otherTaskIsMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val fullscreenTask = createFullscreenTask() @@ -1849,8 +1862,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val fullscreenTask = createFullscreenTask() @@ -1866,8 +1877,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val minimizedTask = setUpFreeformTask() taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = minimizedTask.taskId) val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } @@ -1888,7 +1897,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM @@ -1905,7 +1913,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noTasks_enforceDesktop_fullscreenDisplay_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN @@ -1918,8 +1925,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskHidden(freeformTask) val fullscreenTask = createFullscreenTask() @@ -1928,16 +1933,12 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_fullscreenTask_noOtherTasks_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val fullscreenTask = createFullscreenTask() assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull() } @Test fun handleRequest_fullscreenTask_freeformTaskOnOtherDisplay_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val fullscreenTaskDefaultDisplay = createFullscreenTask(displayId = DEFAULT_DISPLAY) createFreeformTask(displayId = SECOND_DISPLAY) @@ -1947,8 +1948,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val newFreeformTask = createFreeformTask() @@ -1961,8 +1960,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchActiveTask_taskBecomesUndefined() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask = setUpFreeformTask() markTaskHidden(freeformTask) @@ -1977,7 +1974,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchTask_enforceDesktop_freeformDisplay_noWinModeChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM @@ -1992,7 +1988,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_relaunchTask_enforceDesktop_fullscreenDisplay_becomesUndefined() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN @@ -2009,8 +2004,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperDisabled_freeformNotVisible_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask1 = setUpFreeformTask() val freeformTask2 = createFreeformTask() @@ -2026,8 +2019,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val freeformTask1 = setUpFreeformTask() val freeformTask2 = createFreeformTask() @@ -2048,8 +2039,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperDisabled_noOtherTasks_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = createFreeformTask() val result = controller.handleRequest(Binder(), createTransition(task)) @@ -2061,8 +2050,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = createFreeformTask() val result = controller.handleRequest(Binder(), createTransition(task)) @@ -2077,8 +2064,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_dskWallpaperDisabled_freeformOnOtherDisplayOnly_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY) // Second display task createFreeformTask(displayId = SECOND_DISPLAY) @@ -2093,8 +2078,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY) // Second display task createFreeformTask(displayId = SECOND_DISPLAY) @@ -2111,7 +2094,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false) val freeformTask1 = setUpFreeformTask() @@ -2125,7 +2107,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true) val freeformTask1 = setUpFreeformTask() @@ -2139,7 +2120,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_freeformTask_keyguardLocked_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(keyguardManager.isKeyguardLocked).thenReturn(true) val freeformTask = createFreeformTask(displayId = DEFAULT_DISPLAY) @@ -2150,8 +2130,6 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_notOpenOrToFrontTransition_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = TestRunningTaskInfoBuilder() .setActivityType(ACTIVITY_TYPE_STANDARD) @@ -2164,21 +2142,17 @@ class DesktopTasksControllerTest : ShellTestCase() { @Test fun handleRequest_noTriggerTask_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull() } @Test fun handleRequest_triggerTaskNotStandard_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build() assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull() } @Test fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) - val task = TestRunningTaskInfoBuilder() .setActivityType(ACTIVITY_TYPE_STANDARD) @@ -2860,7 +2834,8 @@ class DesktopTasksControllerTest : ShellTestCase() { PointF(200f, -200f), /* inputCoordinate */ Rect(100, -100, 500, 1000), /* currentDragBounds */ Rect(0, 50, 2000, 2000), /* validDragArea */ - Rect() /* dragStartBounds */ ) + Rect() /* dragStartBounds */, + motionEvent) val rectAfterEnd = Rect(100, 50, 500, 1150) verify(transitions) .startTransition( @@ -2895,7 +2870,8 @@ class DesktopTasksControllerTest : ShellTestCase() { PointF(200f, 300f), /* inputCoordinate */ currentDragBounds, /* currentDragBounds */ Rect(0, 50, 2000, 2000) /* validDragArea */, - Rect() /* dragStartBounds */) + Rect() /* dragStartBounds */, + motionEvent) verify(transitions) @@ -3116,13 +3092,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val transition = Binder() whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull())) .thenReturn(transition) - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId))) + .thenReturn(runOnStartTransit) runOpenInstance(immersiveTask, freeformTask.taskId) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3142,10 +3119,19 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + STABLE_BOUNDS.height(), + STABLE_BOUNDS.width(), + displayController + ) } @Test @@ -3164,15 +3150,22 @@ class DesktopTasksControllerTest : ShellTestCase() { STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom ) - controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent) // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds) assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.SNAP_LEFT_MENU, + motionEvent, + task, + expectedBounds.height(), + expectedBounds.width(), + displayController + ) } @Test fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() { - assumeTrue(ENABLE_SHELL_TRANSITIONS) // Set up task to already be in snapped-left bounds val bounds = Rect( STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom @@ -3187,7 +3180,7 @@ class DesktopTasksControllerTest : ShellTestCase() { // Attempt to snap left again val currentDragBounds = Rect(bounds).apply { offset(-100, 0) } - controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT, ResizeTrigger.SNAP_LEFT_MENU, motionEvent) // Assert that task is NOT updated via WCT verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any()) @@ -3200,6 +3193,14 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(bounds), eq(true) ) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.SNAP_LEFT_MENU, + motionEvent, + task, + bounds.height(), + bounds.width(), + displayController + ) } @Test @@ -3210,12 +3211,22 @@ class DesktopTasksControllerTest : ShellTestCase() { } val preDragBounds = Rect(100, 100, 400, 500) val currentDragBounds = Rect(0, 100, 300, 500) + val expectedBounds = + Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom) controller.handleSnapResizingTask( - task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds) + task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent + ) val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds) assertThat(findBoundsChange(wct, task)).isEqualTo( - Rect(STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom)) + expectedBounds + ) + verify(desktopModeEventLogger, times(1)).logTaskResizingStarted( + ResizeTrigger.DRAG_LEFT, + motionEvent, + task, + displayController + ) } @Test @@ -3228,7 +3239,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val currentDragBounds = Rect(0, 100, 300, 500) controller.handleSnapResizingTask( - task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds) + task, SnapPosition.LEFT, mockSurface, currentDragBounds, preDragBounds, motionEvent) verify(mReturnToDragStartAnimator).start( eq(task.taskId), eq(mockSurface), @@ -3236,6 +3247,13 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(preDragBounds), eq(false) ) + verify(desktopModeEventLogger, never()).logTaskResizingStarted( + any(), + any(), + any(), + any(), + any() + ) } @Test @@ -3254,10 +3272,19 @@ class DesktopTasksControllerTest : ShellTestCase() { // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + expectedBounds.height(), + expectedBounds.width(), + displayController + ) } @Test @@ -3265,8 +3292,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds) + verify(desktopModeEventLogger, never()).logTaskResizingEnded( + any(), any(), any(), any(), + any(), any(), any() + ) } @Test @@ -3275,15 +3306,23 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3294,16 +3333,24 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left, boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3314,16 +3361,24 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left, STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @Test @@ -3332,14 +3387,22 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) // Assert last bounds before maximize removed after use assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.MAXIMIZE_BUTTON, + motionEvent, + task, + boundsBeforeMaximize.height(), + boundsBeforeMaximize.width(), + displayController + ) } @@ -3383,7 +3446,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.toggleDesktopTaskFullImmersiveState(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToImmersive(task) } @Test @@ -3393,7 +3456,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.toggleDesktopTaskFullImmersiveState(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task) } @Test @@ -3405,7 +3468,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task.requestedVisibleTypes = WindowInsets.Type.statusBars() controller.onTaskInfoChanged(task) - verify(mockDesktopFullImmersiveTransitionHandler).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(task) } @Test @@ -3417,7 +3480,7 @@ class DesktopTasksControllerTest : ShellTestCase() { task.requestedVisibleTypes = WindowInsets.Type.statusBars() controller.onTaskInfoChanged(task) - verify(mockDesktopFullImmersiveTransitionHandler, never()).moveTaskToNonImmersive(task) + verify(mMockDesktopImmersiveController, never()).moveTaskToNonImmersive(task) } @Test @@ -3426,13 +3489,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val wct = WindowContainerTransaction() val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3442,13 +3506,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val wct = WindowContainerTransaction() val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit) whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition) controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN) - verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(wct, task.displayId, task.taskId) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3457,14 +3522,15 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(background = true) val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) + .thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(task.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3473,14 +3539,15 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(background = false) val runOnStartTransit = RunOnStartTransitionCallback() val transition = Binder() - whenever(mockDesktopFullImmersiveTransitionHandler - .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit) + whenever(mMockDesktopImmersiveController + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))) + .thenReturn(runOnStartTransit) whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition) controller.moveTaskToFront(task.taskId, remoteTransition = null) - verify(mockDesktopFullImmersiveTransitionHandler) - .exitImmersiveIfApplicable(any(), eq(task.displayId)) + verify(mMockDesktopImmersiveController) + .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)) runOnStartTransit.assertOnlyInvocation(transition) } @@ -3493,7 +3560,7 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.handleRequest(binder, createTransition(task)) - verify(mockDesktopFullImmersiveTransitionHandler) + verify(mMockDesktopImmersiveController) .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId)) } @@ -3505,10 +3572,117 @@ class DesktopTasksControllerTest : ShellTestCase() { controller.handleRequest(binder, createTransition(task)) - verify(mockDesktopFullImmersiveTransitionHandler) + verify(mMockDesktopImmersiveController) .exitImmersiveIfApplicable(eq(binder), any(), eq(task.displayId)) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notShowingDesktop_doesNotPlay() { + val triggerTask = setUpFullscreenTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = true + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notOpening_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = true + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_CHANGE, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_notImmersive_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5) + taskRepository.setTaskInFullImmersiveState( + displayId = triggerTask.displayId, + taskId = triggerTask.taskId, + immersive = false + ) + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_fullscreenEntersDesktop_plays() { + // At least one freeform task to be in a desktop. + val existingTask = setUpFreeformTask(displayId = 5) + val triggerTask = setUpFullscreenTask(displayId = 5) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue() + taskRepository.setTaskInFullImmersiveState( + displayId = existingTask.displayId, + taskId = existingTask.taskId, + immersive = true + ) + + assertThat( + controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + ) + ).isTrue() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_fullscreenStaysFullscreen_doesNotPlay() { + val triggerTask = setUpFullscreenTask(displayId = 5) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse() + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_freeformStaysInDesktop_plays() { + // At least one freeform task to be in a desktop. + val existingTask = setUpFreeformTask(displayId = 5) + val triggerTask = setUpFreeformTask(displayId = 5, active = false) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isTrue() + taskRepository.setTaskInFullImmersiveState( + displayId = existingTask.displayId, + taskId = existingTask.taskId, + immersive = true + ) + + assertThat( + controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + ) + ).isTrue() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun shouldPlayDesktopAnimation_freeformExitsDesktop_doesNotPlay() { + val triggerTask = setUpFreeformTask(displayId = 5, active = false) + assertThat(controller.isDesktopModeShowing(triggerTask.displayId)).isFalse() + + assertThat(controller.shouldPlayDesktopAnimation( + TransitionRequestInfo(TRANSIT_OPEN, triggerTask, /* remoteTransition= */ null) + )).isFalse() + } + private class RunOnStartTransitionCallback : ((IBinder) -> Unit) { var invocations = 0 private set @@ -3738,14 +3912,11 @@ class DesktopTasksControllerTest : ShellTestCase() { handlerClass: Class<out TransitionHandler>? = null ): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - if (handlerClass == null) { - verify(transitions).startTransition(eq(type), arg.capture(), isNull()) - } else { - verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) - } + + if (handlerClass == null) { + verify(transitions).startTransition(eq(type), arg.capture(), isNull()) } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) + verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass)) } return arg.value } @@ -3755,43 +3926,27 @@ class DesktopTasksControllerTest : ShellTestCase() { ): WindowContainerTransaction { val arg: ArgumentCaptor<WindowContainerTransaction> = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()) + verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce()) .startTransition(capture(arg), eq(currentBounds)) - } else { - verify(shellTaskOrganizer).applyTransaction(capture(arg)) - } return arg.value } private fun getLatestEnterDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) - } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) - } + verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any()) return arg.value } private fun getLatestDragToDesktopWct(): WindowContainerTransaction { val arg: ArgumentCaptor<WindowContainerTransaction> = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg)) - } else { - verify(shellTaskOrganizer).applyTransaction(capture(arg)) - } + verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg)) return arg.value } private fun getLatestExitDesktopWct(): WindowContainerTransaction { val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) - if (ENABLE_SHELL_TRANSITIONS) { - verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any()) - } else { - verify(shellTaskOrganizer).applyTransaction(arg.capture()) - } + verify(exitDesktopTransitionHandler).startTransition(any(), arg.capture(), any(), any()) return arg.value } @@ -3799,27 +3954,15 @@ class DesktopTasksControllerTest : ShellTestCase() { wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds private fun verifyWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(transitions, never()).startTransition(anyInt(), any(), isNull()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(transitions, never()).startTransition(anyInt(), any(), isNull()) } private fun verifyExitDesktopWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any()) } private fun verifyEnterDesktopWCTNotExecuted() { - if (ENABLE_SHELL_TRANSITIONS) { - verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any()) - } else { - verify(shellTaskOrganizer, never()).applyTransaction(any()) - } + verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any()) } private fun createTransition( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt index 596b76dbdb2e..4d7e47fa51bd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt @@ -303,12 +303,12 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() { + fun addAndGetMinimizeTaskChanges_tasksWithinLimit_noTaskMinimized() { (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() } val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = DEFAULT_DISPLAY, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) @@ -318,31 +318,31 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun addAndGetMinimizeTaskChangesIfNeeded_tasksAboveLimit_backTaskMinimized() { + fun addAndGetMinimizeTaskChanges_tasksAboveLimit_backTaskMinimized() { // The following list will be ordered bottom -> top, as the last task is moved to top last. val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = DEFAULT_DISPLAY, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) - assertThat(minimizedTaskId).isEqualTo(tasks.first()) + assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId) assertThat(wct.hierarchyOps.size).isEqualTo(1) assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REORDER) assertThat(wct.hierarchyOps[0].toTop).isFalse() // Reorder to bottom } @Test - fun addAndGetMinimizeTaskChangesIfNeeded_nonMinimizedTasksWithinLimit_noTaskMinimized() { + fun addAndGetMinimizeTaskChanges_nonMinimizedTasksWithinLimit_noTaskMinimized() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = tasks[0].taskId) val wct = WindowContainerTransaction() val minimizedTaskId = - desktopTasksLimiter.addAndGetMinimizeTaskChangesIfNeeded( + desktopTasksLimiter.addAndGetMinimizeTaskChanges( displayId = 0, wct = wct, newFrontTaskId = setUpFreeformTask().taskId) @@ -352,50 +352,50 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test - fun getTaskToMinimizeIfNeeded_tasksWithinLimit_returnsNull() { + fun getTaskToMinimize_tasksWithinLimit_returnsNull() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) assertThat(minimizedTask).isNull() } @Test - fun getTaskToMinimizeIfNeeded_tasksAboveLimit_returnsBackTask() { + fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() { val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test - fun getTaskToMinimizeIfNeeded_tasksAboveLimit_otherLimit_returnsBackTask() { + fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() { desktopTasksLimiter = DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2, interactionJankMonitor, mContext, handler) val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }) + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test - fun getTaskToMinimizeIfNeeded_withNewTask_tasksAboveLimit_returnsBackTask() { + fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() { val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() } - val minimizedTask = desktopTasksLimiter.getTaskToMinimizeIfNeeded( - visibleFreeformTaskIdsOrderedFrontToBack = tasks.map { it.taskId }, + val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize( + visibleOrderedTasks = tasks.map { it.taskId }, newTaskIdInFront = setUpFreeformTask().taskId) // first == front, last == back - assertThat(minimizedTask).isEqualTo(tasks.last()) + assertThat(minimizedTask).isEqualTo(tasks.last().taskId) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt index 1e105d9588ab..7dbadc9d9083 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode.education +import android.os.SystemProperties import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule @@ -50,6 +51,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.kotlin.any @@ -70,7 +72,10 @@ class AppHandleEducationControllerTest : ShellTestCase() { @JvmField @Rule val extendedMockitoRule = - ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!! + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeStatus::class.java) + .mockStatic(SystemProperties::class.java) + .build()!! @JvmField @Rule val setFlagsRule = SetFlagsRule() private lateinit var educationController: AppHandleEducationController @@ -189,6 +194,29 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) + fun overridePrerequisite_educationViewedAlready_shouldCallShowEducationTooltip() = + testScope.runTest { + // App handle is visible but education has been viewed before. But as we are overriding + // prerequisite conditions, we should show education tooltip. + // Mark education viewed. + testDataStoreFlow.value = + createWindowingEducationProto(educationViewedTimestampMillis = 123L) + val systemPropertiesKey = + "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())) + .thenReturn(true) + setShouldShowAppHandleEducation(true) + + // Simulate app handle visible. + testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false) + // Wait for first tooltip to showup. + waitForBufferDelay() + + verify(mockTooltipController, times(1)).showEducationTooltip(any(), any()) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) fun init_appHandleExpanded_shouldMarkFeatureViewed() = testScope.runTest { setShouldShowAppHandleEducation(false) @@ -454,7 +482,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { .thenReturn(shouldShowAppHandleEducation) /** - * Class under test waits for some seconds before showing education, simulate advance time before + * Class under test waits for some time before showing education, simulate advance time before * verifying or moving forward */ private fun TestScope.waitForBufferDelay() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt index ac994248c962..a3e74e8aed5d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt @@ -19,10 +19,12 @@ package com.android.wm.shell.desktopmode.education import android.app.usage.UsageStats import android.app.usage.UsageStatsManager import android.content.Context +import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableResources import androidx.test.filters.SmallTest +import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository @@ -35,18 +37,26 @@ import com.google.common.truth.Truth.assertThat import kotlin.Int.Companion.MAX_VALUE import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever -/** Tests of [AppHandleEducationFilter] - * Usage: atest AppHandleEducationFilterTest */ +/** Tests of [AppHandleEducationFilter] Usage: atest AppHandleEducationFilterTest */ @SmallTest @RunWith(AndroidTestingRunner::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi class AppHandleEducationFilterTest : ShellTestCase() { + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!! @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository @Mock private lateinit var mockUsageStatsManager: UsageStatsManager private lateinit var educationFilter: AppHandleEducationFilter @@ -209,4 +219,23 @@ class AppHandleEducationFilterTest : ShellTestCase() { // We should not show app handle education if app menu is expanded assertThat(result).isFalse() } + + @Test + fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest { + // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence + // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite + // conditions, #shouldShowAppHandleEducation should return true. + testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())).thenReturn(true) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState()) + + assertThat(result).isTrue() + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt index 9b9703fdf6dc..8495580f42a5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt @@ -115,8 +115,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap).hasSize(2) - assertThat(actualDesktop.getZOrderedTasks(0)).isEqualTo(2) + assertThat(actualDesktop?.tasksByTaskIdMap).hasSize(2) + assertThat(actualDesktop?.getZOrderedTasks(0)).isEqualTo(2) } } @@ -138,7 +138,7 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap[task.taskId]?.desktopTaskState) + assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState) .isEqualTo(DesktopTaskState.MINIMIZED) } } @@ -161,8 +161,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() { freeformTasksInZOrder = freeformTasksInZOrder) val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID) - assertThat(actualDesktop.tasksByTaskIdMap).isEmpty() - assertThat(actualDesktop.zOrderedTasksList).isEmpty() + assertThat(actualDesktop?.tasksByTaskIdMap).isEmpty() + assertThat(actualDesktop?.zOrderedTasksList).isEmpty() } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java index 7ae0bcd13681..90ab2b8285cd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java @@ -43,7 +43,7 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.window.flags.Flags; -import com.android.wm.shell.desktopmode.DesktopFullImmersiveTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.TransitionInfoBuilder; @@ -70,7 +70,7 @@ public class FreeformTaskTransitionObserverTest { @Mock private Transitions mTransitions; @Mock - private DesktopFullImmersiveTransitionHandler mDesktopFullImmersiveTransitionHandler; + private DesktopImmersiveController mDesktopImmersiveController; @Mock private WindowDecorViewModel mWindowDecorViewModel; @Mock @@ -92,7 +92,7 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver = new FreeformTaskTransitionObserver( context, mShellInit, mTransitions, - Optional.of(mDesktopFullImmersiveTransitionHandler), + Optional.of(mDesktopImmersiveController), mWindowDecorViewModel, Optional.of(mTaskChangeListener), mFocusTransitionObserver); final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass( @@ -321,7 +321,7 @@ public class FreeformTaskTransitionObserverTest { @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - public void onTransitionReady_forwardsToDesktopImmersiveHandler() { + public void onTransitionReady_forwardsToDesktopImmersiveController() { final IBinder transition = mock(IBinder.class); final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0).build(); final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); @@ -329,7 +329,38 @@ public class FreeformTaskTransitionObserverTest { mTransitionObserver.onTransitionReady(transition, info, startT, finishT); - verify(mDesktopFullImmersiveTransitionHandler).onTransitionReady(transition, info); + verify(mDesktopImmersiveController).onTransitionReady(transition, info, startT, finishT); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionMerged_forwardsToDesktopImmersiveController() { + final IBinder merged = mock(IBinder.class); + final IBinder playing = mock(IBinder.class); + + mTransitionObserver.onTransitionMerged(merged, playing); + + verify(mDesktopImmersiveController).onTransitionMerged(merged, playing); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionStarting_forwardsToDesktopImmersiveController() { + final IBinder transition = mock(IBinder.class); + + mTransitionObserver.onTransitionStarting(transition); + + verify(mDesktopImmersiveController).onTransitionStarting(transition); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + public void onTransitionFinished_forwardsToDesktopImmersiveController() { + final IBinder transition = mock(IBinder.class); + + mTransitionObserver.onTransitionFinished(transition, /* aborted= */ false); + + verify(mDesktopImmersiveController).onTransitionFinished(transition, /* aborted= */ false); } private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java index 66f8c0b9558d..880ca2ce0cf9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -124,7 +124,7 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm, mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer, mPipDismissTargetHandler, - () -> {}, mPipUiEventLogger, mPhonePipMenuController, + (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController, mMainExecutor, null /* pipPerfHintController */) { @Override public void pilferPointers() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java index 0effc3e3d6b8..6087763b4978 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java @@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -43,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.internal.os.IResultReceiver; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; @@ -141,8 +143,9 @@ public class RecentsTransitionHandlerTest extends ShellTestCase { } @Test - public void testStartSyntheticRecentsTransition_callsOnAnimationStart() throws Exception { + public void testStartSyntheticRecentsTransition_callsOnAnimationStartAndFinishCallback() throws Exception { final IRecentsAnimationRunner runner = mock(IRecentsAnimationRunner.class); + final IResultReceiver finishCallback = mock(IResultReceiver.class); doReturn(new Binder()).when(runner).asBinder(); Bundle options = new Bundle(); options.putBoolean("is_synthetic_recents_transition", true); @@ -151,10 +154,11 @@ public class RecentsTransitionHandlerTest extends ShellTestCase { runner); verify(runner).onAnimationStart(any(), any(), any(), any(), any(), any()); - // Finish and verify no transition remains + // Finish and verify no transition remains and that the provided finish callback is called mRecentsTransitionHandler.findController(transition).finish(true /* toHome */, - false /* sendUserLeaveHint */, null /* finishCb */); + false /* sendUserLeaveHint */, finishCallback); mMainExecutor.flushAll(); + verify(finishCallback).send(anyInt(), any()); assertNull(mRecentsTransitionHandler.findController(transition)); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 175fbd2396e3..1839b8a367fe 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -87,6 +87,8 @@ import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler +import com.android.wm.shell.desktopmode.DesktopModeEventLogger +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition @@ -194,7 +196,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockAppHandleEducationController: AppHandleEducationController @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository + @Mock private lateinit var motionEvent: MotionEvent + @Mock lateinit var displayController: DisplayController + @Mock lateinit var displayLayout: DisplayLayout private lateinit var spyContext: TestableContext + private lateinit var desktopModeEventLogger: DesktopModeEventLogger private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() @@ -224,6 +230,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { shellInit = ShellInit(mockShellExecutor) windowDecorByTaskIdSpy.clear() spyContext.addMockSystemService(InputManager::class.java, mockInputManager) + desktopModeEventLogger = mock<DesktopModeEventLogger>() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, mockShellExecutor, @@ -256,7 +263,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockCaptionHandleRepository, Optional.of(mockActivityOrientationChangeHandler), mockTaskPositionerFactory, - mockFocusTransitionObserver + mockFocusTransitionObserver, + desktopModeEventLogger ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -299,6 +307,10 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { argumentCaptor<DesktopModeKeyguardChangeListener>() verify(mockShellController).addKeyguardChangeListener(keyguardChangedCaptor.capture()) desktopModeOnKeyguardChangedListener = keyguardChangedCaptor.firstValue + whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } } @After @@ -612,7 +624,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { maxOrRestoreListenerCaptor.value.invoke() - verify(mockDesktopTasksController).toggleDesktopTaskSize(decor.mTaskInfo) + verify(mockDesktopTasksController).toggleDesktopTaskSize( + decor.mTaskInfo, + ResizeTrigger.MAXIMIZE_MENU, + null + ) } @Test @@ -647,7 +663,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.LEFT) + eq(SnapPosition.LEFT), + eq(ResizeTrigger.SNAP_LEFT_MENU), + eq(null) ) assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface) } @@ -685,7 +703,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.LEFT) + eq(SnapPosition.LEFT), + eq(ResizeTrigger.SNAP_LEFT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -704,7 +724,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onLeftSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT)) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT), + eq(ResizeTrigger.MAXIMIZE_BUTTON), + eq(null)) verify(mockToast).show() } @@ -725,7 +747,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.RIGHT) + eq(SnapPosition.RIGHT), + eq(ResizeTrigger.SNAP_RIGHT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -763,7 +787,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { eq(decor.mTaskInfo), taskSurfaceCaptor.capture(), eq(currentBounds), - eq(SnapPosition.RIGHT) + eq(SnapPosition.RIGHT), + eq(ResizeTrigger.SNAP_RIGHT_MENU), + eq(null) ) assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @@ -782,7 +808,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onRightSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT)) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT), + eq(ResizeTrigger.MAXIMIZE_BUTTON), + eq(null)) verify(mockToast).show() } @@ -1247,7 +1275,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onClickListenerCaptor.value.onClick(view) verify(mockDesktopTasksController) - .toggleDesktopTaskSize(decor.mTaskInfo) + .toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null) } private fun createOpenTaskDecoration( @@ -1337,7 +1365,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { whenever( mockDesktopModeWindowDecorFactory.create( any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any()) + any(), any(), any(), any(), any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.user).thenReturn(mockUserHandle) @@ -1378,5 +1406,6 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { private const val TAG = "DesktopModeWindowDecorViewModelTests" private val STABLE_INSETS = Rect(0, 100, 0, 0) private val INITIAL_BOUNDS = Rect(0, 0, 100, 100) + private val STABLE_BOUNDS = Rect(0, 0, 1000, 1000) } } 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 320887212f54..0afb6c10b549 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 @@ -106,6 +106,7 @@ import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.CaptionState; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -210,6 +211,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private MultiInstanceHelper mMockMultiInstanceHelper; @Mock private WindowDecorCaptionHandleRepository mMockCaptionHandleRepository; + @Mock + private DesktopModeEventLogger mDesktopModeEventLogger; @Captor private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener; @Captor @@ -1400,7 +1403,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory, - mMockMultiInstanceHelper, mMockCaptionHandleRepository); + mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt index 24f6becc3536..a20a89c644ed 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt @@ -36,6 +36,7 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM +import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.google.common.truth.Truth.assertThat @@ -48,9 +49,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.quality.Strictness +import org.mockito.Mockito.`when` as whenever /** * Tests for [DragPositioningCallbackUtility]. @@ -193,6 +194,62 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialHeightLessThanMin_increasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), + BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) + + // Resize to increased bounds + val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + 20 + val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right + 20) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialHeightMoreThanMax_decreasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), + EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) + + // Resize to decreased bounds. + val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 + val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + 20 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top + 20) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat()) @@ -211,13 +268,68 @@ class DragPositioningCallbackUtilityTest { ) ) - assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialWidthLessThanMin_increasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), + BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) + + // Resize to increased bounds. + val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 + val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + 20 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom + 20) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) + fun testChangeBounds_unresizeableApp_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), + EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) + + // Resize to decreased bounds. + val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 + val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is in direction of desired range. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_LEFT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top + 10) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) + } + @Test fun testChangeBoundsDoesNotChangeHeightWhenNegative() { @@ -427,6 +539,60 @@ class DragPositioningCallbackUtilityTest { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMinHeight_initialHeightLessThanMin_increasingHeight_resizeAllowed() { + val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), + BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) + + // Attempt to increase height. + val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is increasing height closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMinWidth_initialWidthLessThanMin_increasingBounds_resizeAllowed() { + val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), + BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) + val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) + + // Attempt to increase width. + val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 + val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is increasing width closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, + repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) } initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1) @@ -547,6 +713,61 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMaxHeight_initialHeightMoreThanMax_decreasingHeight_resizeAllowed() { + mockWindowDecoration.mTaskInfo.isResizeable = false + val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), + EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) + + // Attempt to decrease height + val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 + val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is decreasing height closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom ) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) + fun testMaxHeight_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { + val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), + EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) + val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) + + // Attempt to decrease width. + val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 + val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) + + // Resize should be allowed as drag is decreasing width closer to valid region. + assertTrue( + DragPositioningCallbackUtility.changeBounds( + CTRL_TYPE_LEFT or CTRL_TYPE_TOP, + repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, + mockDisplayController, mockWindowDecoration + ) + ) + + assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) + assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top) + assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) + assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) + } + private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID @@ -571,6 +792,10 @@ class DragPositioningCallbackUtilityTest { private const val NAVBAR_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) + private val BELOW_MIN_WIDTH_BOUNDS = Rect(0, 0, 50, 100) + private val BELOW_MIN_HEIGHT_BOUNDS = Rect(0, 0, 100, 50) + private val EXCEEDS_MAX_WIDTH_BOUNDS = Rect(0, 0, 3000, 1500) + private val EXCEEDS_MAX_HEIGHT_BOUNDS = Rect(0, 0, 1000, 2000) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, 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 fb17ae93030d..cb7fadee9822 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 @@ -83,6 +83,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.tests.R; import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer; @@ -138,6 +139,8 @@ public class WindowDecorationTests extends ShellTestCase { private SurfaceSyncGroup mMockSurfaceSyncGroup; @Mock private SurfaceControl mMockTaskSurface; + @Mock + private DesktopModeEventLogger mDesktopModeEventLogger; private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions = new ArrayList<>(); @@ -1014,7 +1017,7 @@ public class WindowDecorationTests extends ShellTestCase { new MockObjectSupplier<>(mMockSurfaceControlTransactions, () -> mock(SurfaceControl.Transaction.class)), () -> mMockWindowContainerTransaction, () -> mMockTaskSurface, - mMockSurfaceControlViewHostFactory); + mMockSurfaceControlViewHostFactory, mDesktopModeEventLogger); } private class MockObjectSupplier<T> implements Supplier<T> { @@ -1054,11 +1057,12 @@ public class WindowDecorationTests extends ShellTestCase { Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, - SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + SurfaceControlViewHostFactory surfaceControlViewHostFactory, + DesktopModeEventLogger desktopModeEventLogger) { super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, - surfaceControlViewHostFactory); + surfaceControlViewHostFactory, desktopModeEventLogger); } @Override diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index a39f30bbad1f..1bc15d72bacc 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -262,6 +262,8 @@ cc_test { "tests/data/**/*.apk", "tests/data/**/*.arsc", "tests/data/**/*.idmap", + ], + device_common_data: [ ":FrameworkResourcesSparseTestApp", ":FrameworkResourcesNotSparseTestApp", ], diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 23097f634464..c7a7ed2a885e 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -17,7 +17,6 @@ #include "Color.h" #include <Properties.h> -#include <aidl/android/hardware/graphics/common/Dataspace.h> #include <android/hardware_buffer.h> #include <android/native_window.h> #include <ui/ColorSpace.h> @@ -222,8 +221,7 @@ android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType c if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { return HAL_DATASPACE_BT2020; } else if (nearlyEqual(fn, SkNamedTransferFn::kSRGB)) { - return static_cast<android_dataspace>( - ::aidl::android::hardware::graphics::common::Dataspace::DISPLAY_BT2020); + return static_cast<android_dataspace>(HAL_DATASPACE_DISPLAY_BT2020); } } diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index 28856c87f7c6..8af4b7e8f4c8 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -60,6 +60,7 @@ cc_library { "//apex_available:platform", "com.android.os.statsd", "test_com.android.os.statsd", + "com.android.uprobestats", ], } diff --git a/lint-baseline.xml b/lint-baseline.xml index 0320aabd7199..8253c1f8f0b3 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -11265,4 +11265,15 @@ column="24"/> </issue> -</issues>
\ No newline at end of file + <issue + id="SimpleManualPermissionEnforcement" + message="INetworkScoreCache permission check should be converted to @EnforcePermission annotation" + errorLine1=" mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java" + line="250" + column="9"/> + </issue> + +</issues> diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java index 13876ad6c3c7..e1fbfea19235 100644 --- a/media/java/android/media/AudioDeviceVolumeManager.java +++ b/media/java/android/media/AudioDeviceVolumeManager.java @@ -16,8 +16,11 @@ package android.media; +import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL; + import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -434,6 +437,83 @@ public class AudioDeviceVolumeManager { /** * @hide + * Sets the input gain index for a particular AudioDeviceAttributes. + * TODO(b/364923030): create InputVolumeInfo on top of VolumeInfo rather than using index to + * handle volume information, to solve issues e.g. gain index ranges might be different for + * different categories of devices. + */ + @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) { + try { + getService().setInputGainIndex(ada, index); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Gets the input gain index for a particular AudioDeviceAttributes. + */ + @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getInputGainIndex(@NonNull AudioDeviceAttributes ada) { + try { + return getService().getInputGainIndex(ada); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Gets the maximum input gain index for input device. + */ + @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getMaxInputGainIndex() { + try { + return getService().getMaxInputGainIndex(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Gets the minimum input gain index for input device. + */ + @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getMinInputGainIndex() { + try { + return getService().getMinInputGainIndex(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Indicates if an input device does not support input gain control. + * <p>The following APIs have no effect when input gain is fixed: + * <ul> + * <li>{@link #setInputGainIndex(AudioDeviceAttributes, int)} + * </ul> + */ + @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isInputGainFixed(@NonNull AudioDeviceAttributes ada) { + try { + return getService().isInputGainFixed(ada); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide * Return human-readable name for volume behavior * @param behavior one of the volume behaviors defined in AudioManager * @return a string for the given behavior diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java index 2b6f72eabeec..3e456a65c2e4 100644 --- a/media/java/android/media/AudioHalVersionInfo.java +++ b/media/java/android/media/AudioHalVersionInfo.java @@ -84,7 +84,7 @@ public final class AudioHalVersionInfo implements Parcelable, Comparable<AudioHa * there is a change to supported versions. */ public static final @NonNull List<AudioHalVersionInfo> VERSIONS = - List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0); + List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0); private static final String TAG = "AudioHalVersionInfo"; private AudioHalVersion mHalVersion = new AudioHalVersion(); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index c22b67462af2..9fd3f5beb25c 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -189,6 +189,21 @@ interface IAudioService { void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag); + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + void setInputGainIndex(in AudioDeviceAttributes ada, int index); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + int getInputGainIndex(in AudioDeviceAttributes ada); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + int getMaxInputGainIndex(); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + int getMinInputGainIndex(); + + @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED") + boolean isInputGainFixed(in AudioDeviceAttributes ada); + oneway void setMicrophoneMuteFromSwitch(boolean on); void setRingerModeExternal(int ringerMode, String caller); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 8ff4305a9817..3a19f466f7c1 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -462,6 +462,33 @@ public final class MediaCodecInfo { @SuppressLint("AllUpper") public static final int COLOR_FormatYUVP010 = 54; + /** + * P210 is 10-bit-per component 4:2:2 YCbCr semiplanar format. + * <p> + * This format uses 32 allocated bits per pixel with 20 bits of + * data per pixel. Chroma planes are subsampled by 2 both + * horizontally. Each chroma and luma component + * has 16 allocated bits in little-endian configuration with 10 + * MSB of actual data. + * + * <pre> + * byte byte + * <--------- i --------> | <------ i + 1 ------> + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | UNUSED | Y/Cb/Cr | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 0 5 6 7 0 7 + * bit + * </pre> + * + * Use this format with {@link Image}. This format corresponds + * to {@link android.graphics.ImageFormat#YCBCR_P210}. + * <p> + */ + @SuppressLint("AllUpper") + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int COLOR_FormatYUVP210 = 60; + /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java index 41e9b65da93a..11bd221ec696 100644 --- a/media/java/android/media/Utils.java +++ b/media/java/android/media/Utils.java @@ -719,6 +719,9 @@ public class Utils { * @return {@code true} if the Uri has vibration parameter */ public static boolean hasVibration(Uri ringtoneUri) { + if (ringtoneUri == null) { + return false; + } final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM); return vibrationUriString != null; } @@ -730,7 +733,10 @@ public class Utils { * @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter * is not found. */ - public static Uri getVibrationUri(Uri ringtoneUri) { + public static @Nullable Uri getVibrationUri(Uri ringtoneUri) { + if (ringtoneUri == null) { + return null; + } final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM); if (vibrationUriString == null) { return null; diff --git a/media/java/android/media/audiofx/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java index 7abada07181d..09863450a717 100644 --- a/media/java/android/media/audiofx/Equalizer.java +++ b/media/java/android/media/audiofx/Equalizer.java @@ -153,9 +153,8 @@ public class Equalizer extends AudioEffect { param[0] = PARAM_GET_PRESET_NAME; for (int i = 0; i < mNumPresets; i++) { param[1] = i; - checkStatus(getParameter(param, value)); - int length = 0; - while (value[length] != 0) length++; + final int length = getParameter(param, value); + checkStatus(length); try { mPresetNames[i] = new String(value, 0, length, "ISO-8859-1"); } catch (java.io.UnsupportedEncodingException e) { diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index fd677acf4ee1..898a8bf02edb 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -16,11 +16,13 @@ package android.media.tv.tuner.frontend; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.media.tv.flags.Flags; import android.media.tv.tuner.Lnb; import android.media.tv.tuner.TunerVersionChecker; @@ -61,7 +63,7 @@ public class FrontendStatus { FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO, FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST, FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS, - FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS}) + FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXT}) @Retention(RetentionPolicy.SOURCE) public @interface FrontendStatusType {} @@ -311,6 +313,13 @@ public class FrontendStatus { public static final int FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO = android.hardware.tv.tuner.FrontendStatusType.ATSC3_ALL_PLP_INFO; + /** + * Standard extension. + */ + @FlaggedApi(Flags.FLAG_TUNER_W_APIS) + public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = + android.hardware.tv.tuner.FrontendStatusType.STANDARD_EXT; + /** @hide */ @IntDef(value = { AtscFrontendSettings.MODULATION_UNDEFINED, @@ -558,6 +567,7 @@ public class FrontendStatus { private Long mIptvPacketsReceived; private Integer mIptvWorstJitterMs; private Integer mIptvAverageJitterMs; + private StandardExt mStandardExt; // Constructed and fields set by JNI code. private FrontendStatus() { @@ -1273,4 +1283,27 @@ public class FrontendStatus { } return mIptvAverageJitterMs; } + /** + * Gets the standard extension. + * + * <p>The tuner standard DVB-T has the extension DVB-T2, and the standard DVB-S has the + * extensions DVB-S2 and DVB-S2X. This method returns the current standard extension within the + * same standard series. This frontend status is reported when the standard extension + * transitions to another during playback. + * + * <p>This query is supported only by Tuner HAL 4.0 or higher. Use + * {@link TunerVersionChecker#getTunerVersion()} to check the version. + * + * @return The current standard extension. + */ + @NonNull + @FlaggedApi(Flags.FLAG_TUNER_W_APIS) + public StandardExt getStandardExt() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_4_0, "StandardExt status"); + if (mStandardExt == null) { + throw new IllegalStateException("StandardExt status is empty"); + } + return mStandardExt; + } } diff --git a/media/java/android/media/tv/tuner/frontend/StandardExt.java b/media/java/android/media/tv/tuner/frontend/StandardExt.java new file mode 100644 index 000000000000..490727278b46 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/StandardExt.java @@ -0,0 +1,75 @@ +/* + * 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.media.tv.tuner.frontend; + +import android.annotation.FlaggedApi; +import android.annotation.SystemApi; +import android.hardware.tv.tuner.FrontendDvbsStandard; +import android.hardware.tv.tuner.FrontendDvbtStandard; +import android.media.tv.flags.Flags; + +/** + * Standard extension for the standard DVB-T and DVB-S series. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_TUNER_W_APIS) +public final class StandardExt { + private final int mDvbsStandardExt; + private final int mDvbtStandardExt; + + /** + * Private constructor called by JNI only. + */ + private StandardExt(int dvbsStandardExt, int dvbtStandardExt) { + mDvbsStandardExt = dvbsStandardExt; + mDvbtStandardExt = dvbtStandardExt; + } + + /** + * Gets the DVB-S standard extension within the DVB-S standard series. + * + * @return An integer representing the standard, such as + * {@link DvbsFrontendSettings#STANDARD_S}. + * + * @see android.media.tv.tuner.frontend.DvbsFrontendSettings + */ + @DvbsFrontendSettings.Standard + public int getDvbsStandardExt() { + if (mDvbsStandardExt == FrontendDvbsStandard.UNDEFINED) { + throw new IllegalStateException("No DVB-S standard transition"); + } + return mDvbsStandardExt; + } + + /** + * Gets the DVB-T standard extension within the DVB-T standard series. + * + * @return An integer representing the standard, such as + * {@link DvbtFrontendSettings#STANDARD_T}. + * + * @see android.media.tv.tuner.frontend.DvbtFrontendSettings + */ + @DvbtFrontendSettings.Standard + public int getDvbtStandardExt() { + if (mDvbtStandardExt == FrontendDvbtStandard.UNDEFINED) { + throw new IllegalStateException("No DVB-T standard transition"); + } + return mDvbtStandardExt; + } +} diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 9e1e2c39ee5b..80ca4f239a26 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -144,6 +144,7 @@ #include <aidl/android/hardware/tv/tuner/FrontendScanAtsc3PlpInfo.h> #include <aidl/android/hardware/tv/tuner/FrontendScanMessageStandard.h> #include <aidl/android/hardware/tv/tuner/FrontendSpectralInversion.h> +#include <aidl/android/hardware/tv/tuner/FrontendStandardExt.h> #include <aidl/android/hardware/tv/tuner/FrontendStatus.h> #include <aidl/android/hardware/tv/tuner/FrontendStatusAtsc3PlpInfo.h> #include <aidl/android/hardware/tv/tuner/FrontendStatusType.h> @@ -302,6 +303,7 @@ using ::aidl::android::hardware::tv::tuner::FrontendRollOff; using ::aidl::android::hardware::tv::tuner::FrontendScanAtsc3PlpInfo; using ::aidl::android::hardware::tv::tuner::FrontendScanMessageStandard; using ::aidl::android::hardware::tv::tuner::FrontendSpectralInversion; +using ::aidl::android::hardware::tv::tuner::FrontendStandardExt; using ::aidl::android::hardware::tv::tuner::FrontendStatus; using ::aidl::android::hardware::tv::tuner::FrontendStatusAtsc3PlpInfo; using ::aidl::android::hardware::tv::tuner::FrontendStatusType; @@ -2937,6 +2939,33 @@ jobject JTuner::getFrontendStatus(jintArray types) { env->SetObjectField(statusObj, field, newIntegerObj.get()); break; } + case FrontendStatus::Tag::standardExt: { + jfieldID field = env->GetFieldID(clazz, "mStandardExt", + "Landroid/media/tv/tuner/frontend/StandardExt;"); + ScopedLocalRef standardExtClazz(env, + env->FindClass("android/media/tv/tuner/frontend/StandardExt")); + jmethodID initStandardExt = env->GetMethodID(standardExtClazz.get(), "<init>", + "(II)V"); + + jint dvbsStandardExt = static_cast<jint>(FrontendDvbsStandard::UNDEFINED); + jint dvbtStandardExt = static_cast<jint>(FrontendDvbtStandard::UNDEFINED); + FrontendStandardExt standardExt = s.get<FrontendStatus::Tag::standardExt>(); + switch (standardExt.getTag()) { + case FrontendStandardExt::Tag::dvbsStandardExt: { + dvbsStandardExt = static_cast<jint>(standardExt + .get<FrontendStandardExt::Tag::dvbsStandardExt>()); + break; + } + case FrontendStandardExt::Tag::dvbtStandardExt: { + dvbtStandardExt = static_cast<jint>(standardExt + .get<FrontendStandardExt::Tag::dvbtStandardExt>()); + break; + } + } + ScopedLocalRef standardExtObj(env, env->NewObject(standardExtClazz.get(), + initStandardExt, dvbsStandardExt, dvbtStandardExt)); + env->SetObjectField(statusObj, field, standardExtObj.get()); + } } } return statusObj; diff --git a/mime/Android.bp b/mime/Android.bp index 757862b998b4..20110f1dfb47 100644 --- a/mime/Android.bp +++ b/mime/Android.bp @@ -92,7 +92,7 @@ filegroup { visibility: [ "//visibility:private", ], - srcs: [ + device_common_srcs: [ ":debian.mime.types.minimized", ":android.mime.types.minimized", ":vendor.mime.types.minimized", diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 095d7d1145ae..15f77cebf3ba 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -39,6 +39,7 @@ #include <utils/SystemClock.h> #include <chrono> +#include <future> #include <set> #include <utility> #include <vector> @@ -104,6 +105,7 @@ private: size_t mAvailableSlots GUARDED_BY(sHintMutex) = 0; bool mHalSupported = true; HalMessageQueue::MemTransaction mFmqTransaction GUARDED_BY(sHintMutex); + std::future<bool> mChannelCreationFinished; }; struct APerformanceHintManager { @@ -218,6 +220,8 @@ APerformanceHintManager::~APerformanceHintManager() { } APerformanceHintManager* APerformanceHintManager::getInstance() { + static std::once_flag creationFlag; + static APerformanceHintManager* instance = nullptr; if (gHintManagerForTesting) { return gHintManagerForTesting.get(); } @@ -226,7 +230,7 @@ APerformanceHintManager* APerformanceHintManager::getInstance() { std::shared_ptr<APerformanceHintManager>(create(*gIHintManagerForTesting)); return gHintManagerForTesting.get(); } - static APerformanceHintManager* instance = create(nullptr); + std::call_once(creationFlag, []() { instance = create(nullptr); }); return instance; } @@ -522,25 +526,28 @@ bool FMQWrapper::isSupported() { } bool FMQWrapper::startChannel(IHintManager* manager) { - if (isSupported() && !isActive()) { - std::optional<hal::ChannelConfig> config; - auto ret = manager->getSessionChannel(mToken, &config); - if (ret.isOk() && config.has_value()) { - std::scoped_lock lock{sHintMutex}; - mQueue = std::make_shared<HalMessageQueue>(config->channelDescriptor, true); - if (config->eventFlagDescriptor.has_value()) { - mFlagQueue = std::make_shared<HalFlagQueue>(*config->eventFlagDescriptor, true); - android::hardware::EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), - &mEventFlag); - mWriteMask = config->writeFlagBitmask; + if (isSupported() && !isActive() && manager->isRemote()) { + mChannelCreationFinished = std::async(std::launch::async, [&, this, manager]() { + std::optional<hal::ChannelConfig> config; + auto ret = manager->getSessionChannel(mToken, &config); + if (ret.isOk() && config.has_value()) { + std::scoped_lock lock{sHintMutex}; + mQueue = std::make_shared<HalMessageQueue>(config->channelDescriptor, true); + if (config->eventFlagDescriptor.has_value()) { + mFlagQueue = std::make_shared<HalFlagQueue>(*config->eventFlagDescriptor, true); + android::hardware::EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), + &mEventFlag); + mWriteMask = config->writeFlagBitmask; + } + updatePersistentTransaction(); + } else if (ret.isOk() && !config.has_value()) { + ALOGV("FMQ channel enabled but unsupported."); + setUnsupported(); + } else { + ALOGE("%s: FMQ channel initialization failed: %s", __FUNCTION__, ret.getMessage()); } - updatePersistentTransaction(); - } else if (ret.isOk() && !config.has_value()) { - ALOGV("FMQ channel enabled but unsupported."); - setUnsupported(); - } else { - ALOGE("%s: FMQ channel initialization failed: %s", __FUNCTION__, ret.getMessage()); - } + return true; + }); } return isActive(); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 905d6f68a8a0..520ba896f01f 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -923,12 +923,15 @@ public final class NfcOemExtension { } private @CardEmulation.ProtocolAndTechnologyRoute int routeStringToInt(String route) { - return switch (route) { - case "DH" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; - case "eSE" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; - case "SIM" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; - default -> throw new IllegalStateException("Unexpected value: " + route); - }; + if (route.equals("DH")) { + return PROTOCOL_AND_TECHNOLOGY_ROUTE_DH; + } else if (route.startsWith("eSE")) { + return PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE; + } else if (route.startsWith("SIM")) { + return PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC; + } else { + throw new IllegalStateException("Unexpected value: " + route); + } } private class ReceiverWrapper<T> implements Consumer<T> { diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp index f03c3f6eb647..57d75f596485 100644 --- a/omapi/aidl/vts/functional/AccessControlApp/Android.bp +++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp @@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_nfc", default_applicable_licenses: ["Android-Apache-2.0"], } diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp index c41479f9e9cf..8ee55ff56bb6 100644 --- a/omapi/aidl/vts/functional/omapi/Android.bp +++ b/omapi/aidl/vts/functional/omapi/Android.bp @@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_fwk_nfc", default_applicable_licenses: ["Android-Apache-2.0"], } diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index 9a8261c20f8a..8e5ae2082ac1 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -24,9 +24,12 @@ import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecov import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -94,6 +97,8 @@ import java.util.concurrent.TimeUnit; * be notified. * @hide */ +@FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY) +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public class PackageWatchdog { private static final String TAG = "PackageWatchdog"; @@ -351,7 +356,7 @@ public class PackageWatchdog { * * <p>If monitoring a package supporting explicit health check, at the end of the monitoring * duration if {@link #onHealthCheckPassed} was never called, - * {@link PackageHealthObserver#execute} will be called as if the package failed. + * {@link PackageHealthObserver#onExecuteHealthCheckMitigation} will be called as if the package failed. * * <p>If {@code observer} is already monitoring a package in {@code packageNames}, * the monitoring window of that package will be reset to {@code durationMs} and the health @@ -514,8 +519,8 @@ public class PackageWatchdog { maybeExecute(currentObserverToNotify, versionedPackage, failureReason, currentObserverImpact, mitigationCount); } else { - currentObserverToNotify.execute(versionedPackage, - failureReason, mitigationCount); + currentObserverToNotify.onExecuteHealthCheckMitigation( + versionedPackage, failureReason, mitigationCount); } } } @@ -550,7 +555,8 @@ public class PackageWatchdog { maybeExecute(currentObserverToNotify, failingPackage, failureReason, currentObserverImpact, /*mitigationCount=*/ 1); } else { - currentObserverToNotify.execute(failingPackage, failureReason, 1); + currentObserverToNotify.onExecuteHealthCheckMitigation(failingPackage, + failureReason, 1); } } } @@ -564,7 +570,8 @@ public class PackageWatchdog { synchronized (mLock) { mLastMitigation = mSystemClock.uptimeMillis(); } - currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount); + currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason, + mitigationCount); } } @@ -626,12 +633,12 @@ public class PackageWatchdog { currentObserverInternal.setBootMitigationCount( currentObserverMitigationCount); saveAllObserversBootMitigationCountToMetadata(METADATA_FILE); - currentObserverToNotify.executeBootLoopMitigation( + currentObserverToNotify.onExecuteBootLoopMitigation( currentObserverMitigationCount); } else { mBootThreshold.setMitigationCount(mitigationCount); mBootThreshold.saveMitigationCountToMetadata(); - currentObserverToNotify.executeBootLoopMitigation(mitigationCount); + currentObserverToNotify.onExecuteBootLoopMitigation(mitigationCount); } } } @@ -717,7 +724,9 @@ public class PackageWatchdog { return mPackagesExemptFromImpactLevelThreshold; } - /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. + /** + * Possible severity values of the user impact of a + * {@link PackageHealthObserver#onExecuteHealthCheckMitigation}. * @hide */ @Retention(SOURCE) @@ -753,6 +762,7 @@ public class PackageWatchdog { } /** Register instances of this interface to receive notifications on package failure. */ + @SuppressLint({"CallbackName"}) public interface PackageHealthObserver { /** * Called when health check fails for the {@code versionedPackage}. @@ -765,7 +775,7 @@ public class PackageWatchdog { * * * @return any one of {@link PackageHealthObserverImpact} to express the impact - * to the user on {@link #execute} + * to the user on {@link #onExecuteHealthCheckMitigation} */ @PackageHealthObserverImpact int onHealthCheckFailed( @Nullable VersionedPackage versionedPackage, @@ -773,7 +783,10 @@ public class PackageWatchdog { int mitigationCount); /** - * Executes mitigation for {@link #onHealthCheckFailed}. + * This would be called after {@link #onHealthCheckFailed}. + * This is called only if current observer returned least + * {@link PackageHealthObserverImpact} mitigation for failed health + * check. * * @param versionedPackage the package that is failing. This may be null if a native * service is crashing. @@ -782,7 +795,7 @@ public class PackageWatchdog { * (including this time). * @return {@code true} if action was executed successfully, {@code false} otherwise */ - boolean execute(@Nullable VersionedPackage versionedPackage, + boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage versionedPackage, @FailureReasons int failureReason, int mitigationCount); @@ -798,11 +811,14 @@ public class PackageWatchdog { } /** - * Executes mitigation for {@link #onBootLoop} + * This would be called after {@link #onBootLoop}. + * This is called only if current observer returned least + * {@link PackageHealthObserverImpact} mitigation for fixing boot loop + * * @param mitigationCount the number of times mitigation has been attempted for this * boot loop (including this time). */ - default boolean executeBootLoopMitigation(int mitigationCount) { + default boolean onExecuteBootLoopMitigation(int mitigationCount) { return false; } @@ -1083,7 +1099,7 @@ public class PackageWatchdog { if (versionedPkg != null) { Slog.i(TAG, "Explicit health check failed for package " + versionedPkg); - registeredObserver.execute(versionedPkg, + registeredObserver.onExecuteHealthCheckMitigation(versionedPkg, PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1); } } diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java index f1b2f6b38efa..f1103e19c90e 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java @@ -728,7 +728,7 @@ public class RescueParty { } @Override - public boolean execute(@Nullable VersionedPackage failedPackage, + public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { if (isDisabled()) { return false; @@ -796,7 +796,7 @@ public class RescueParty { } @Override - public boolean executeBootLoopMitigation(int mitigationCount) { + public boolean onExecuteBootLoopMitigation(int mitigationCount) { if (isDisabled()) { return false; } diff --git a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 2931652ee081..8277e573e7c2 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -19,8 +19,11 @@ package com.android.server.rollback; import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.AnyThread; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.WorkerThread; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -75,6 +78,9 @@ import java.util.function.Consumer; * * @hide */ +@FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY) +@SuppressLint({"CallbackName"}) +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final String TAG = "RollbackPackageHealthObserver"; private static final String NAME = "rollback-observer"; @@ -148,7 +154,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve // Note: For non-native crashes the rollback-all step has higher impact impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } else if (getAvailableRollback(failedPackage) != null) { - // Rollback is available, we may get a callback into #execute + // Rollback is available, we may get a callback into #onExecuteHealthCheckMitigation impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; } else if (anyRollbackAvailable) { // If any rollbacks are available, we will commit them @@ -165,7 +171,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean execute(@Nullable VersionedPackage failedPackage, + public boolean onExecuteHealthCheckMitigation(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason, int mitigationCount) { Slog.i(TAG, "Executing remediation." + " failedPackage: " @@ -219,7 +225,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean executeBootLoopMitigation(int mitigationCount) { + public boolean onExecuteBootLoopMitigation(int mitigationCount) { if (Flags.recoverabilityDetection()) { List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index 5b3d47e9f74d..bd892637a5eb 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -55,7 +55,7 @@ android_app { ], } -genrule { +java_genrule { name: "generate_priv_manifest", srcs: [ "shim_priv/AndroidManifest.xml", @@ -169,7 +169,7 @@ android_app { min_sdk_version: "24", } -genrule { +java_genrule { name: "generate_shim_manifest", srcs: [ "shim/AndroidManifest.xml", diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index bfc00bb8b94d..b48c55ddfef0 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -329,18 +329,6 @@ public final class RemotePrintDocument { disconnectFromRemoteDocument(); } - public void kill(String reason) { - if (DEBUG) { - Log.i(LOG_TAG, "[CALLED] kill()"); - } - - try { - mPrintDocumentAdapter.kill(reason); - } catch (RemoteException re) { - Log.e(LOG_TAG, "Error calling kill()", re); - } - } - public boolean isUpdating() { return mState == STATE_UPDATING || mState == STATE_CANCELING; } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index c4173ed999f3..bd2b5ec8436e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -514,8 +514,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY); setState(STATE_UPDATE_FAILED); - - mPrintedDocument.kill(message); + if (DEBUG) { + Log.i(LOG_TAG, "PrintJob state[" + PrintJobInfo.STATE_FAILED + "] reason: " + message); + } + PrintSpoolerService spooler = mSpoolerProvider.getSpooler(); + spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_FAILED, message); + mPrintedDocument.finish(); } @Override diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt index ec903179f496..9cb0ebb995a8 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt @@ -199,3 +199,19 @@ open class KeyedDataObservable<K> : KeyedObservable<K> { } } } + +/** [KeyedObservable] with no-op implementations for all interfaces. */ +open class NoOpKeyedObservable<K> : KeyedObservable<K> { + + override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) = true + + override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) = true + + override fun removeObserver(observer: KeyedObserver<K?>) = true + + override fun removeObserver(key: K, observer: KeyedObserver<K>) = true + + override fun notifyChange(reason: Int) {} + + override fun notifyChange(key: K, reason: Int) {} +} diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index 2e3ee32e2efb..e3f8fbb88a65 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -20,6 +20,8 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 3e0e18488f36..255b2c92e709 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -20,6 +20,8 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml new file mode 100644 index 000000000000..94c6924a02f2 --- /dev/null +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:importantForAccessibility="no"> + + <com.android.settingslib.widget.MainSwitchBar + android:id="@+id/settingslib_main_switch_bar" + android:visibility="gone" + android:layout_height="wrap_content" + android:layout_width="match_parent" /> + +</FrameLayout> + + diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index 7c0eaeaca3de..bf34db93298b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -18,7 +18,11 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="match_parent"> + android:layout_width="match_parent" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> <TextView android:id="@+id/switch_text" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml index fa908a4ed6c8..bef6e352d854 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml @@ -18,10 +18,6 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" - android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingRight="?android:attr/listPreferredItemPaddingRight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:importantForAccessibility="no"> <com.android.settingslib.widget.MainSwitchBar diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 3394874797e3..83858d9c9c54 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -81,7 +81,11 @@ public class MainSwitchPreference extends TwoStatePreference } private void init(Context context, AttributeSet attrs) { - setLayoutResource(R.layout.settingslib_main_switch_layout); + boolean isExpressive = SettingsThemeHelper.isExpressiveTheme(context); + int resId = isExpressive + ? R.layout.settingslib_expressive_main_switch_layout + : R.layout.settingslib_main_switch_layout; + setLayoutResource(resId); mSwitchChangeListeners.add(this); if (attrs != null) { final TypedArray a = context.obtainStyledAttributes(attrs, diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt index 51a85803c6ed..6c11e6997fba 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt @@ -161,14 +161,14 @@ interface DiscreteIntValue : DiscreteValue<Int> { /** Value is between a range. */ interface RangeValue : ValueDescriptor { /** The lower bound (inclusive) of the range. */ - val minValue: Int + fun getMinValue(context: Context): Int /** The upper bound (inclusive) of the range. */ - val maxValue: Int + fun getMaxValue(context: Context): Int /** The increment step within the range. 0 means unset, which implies step size is 1. */ - val incrementStep: Int - get() = 0 + fun getIncrementStep(context: Context) = 0 - override fun isValidValue(context: Context, index: Int) = index in minValue..maxValue + override fun isValidValue(context: Context, index: Int) = + index in getMinValue(context)..getMaxValue(context) } diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt index 98cba1cf098a..43d7dfa4a158 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt @@ -17,6 +17,9 @@ package com.android.settingslib.metadata import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.os.Bundle /** * Interface to provide dynamic preference title. @@ -82,25 +85,62 @@ interface PreferenceRestrictionProvider { interface PreferenceLifecycleProvider { /** - * Called when preference is attached to UI. + * Callbacks of preference fragment `onCreate`. * - * Subclass could override this API to register runtime condition listeners, and invoke - * `onPreferenceStateChanged(this)` on the given [preferenceStateObserver] to update UI when - * internal state (e.g. availability, enabled state, title, summary) is changed. + * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal + * state (e.g. availability, enabled state, title, summary) is changed. */ - fun onAttach(context: Context, preferenceStateObserver: PreferenceStateObserver) + fun onCreate(context: PreferenceLifecycleContext) {} /** - * Called when preference is detached from UI. + * Callbacks of preference fragment `onStart`. * - * Clean up and release resource. + * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal + * state (e.g. availability, enabled state, title, summary) is changed. */ - fun onDetach(context: Context) + fun onStart(context: PreferenceLifecycleContext) {} - /** Observer of preference state. */ - interface PreferenceStateObserver { + /** + * Callbacks of preference fragment `onResume`. + * + * Invoke [PreferenceLifecycleContext.notifyPreferenceChange] to update UI when any internal + * state (e.g. availability, enabled state, title, summary) is changed. + */ + fun onResume(context: PreferenceLifecycleContext) {} + + /** Callbacks of preference fragment `onPause`. */ + fun onPause(context: PreferenceLifecycleContext) {} + + /** Callbacks of preference fragment `onStop`. */ + fun onStop(context: PreferenceLifecycleContext) {} - /** Callbacks when preference state is changed. */ - fun onPreferenceStateChanged(preference: PreferenceMetadata) - } + /** Callbacks of preference fragment `onDestroy`. */ + fun onDestroy(context: PreferenceLifecycleContext) {} + + /** + * Receives the result from a previous call of + * [PreferenceLifecycleContext.startActivityForResult]. + * + * @return true if the result is handled + */ + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = false +} + +/** + * [Context] for preference lifecycle. + * + * A preference fragment is associated with a [PreferenceLifecycleContext] only. + */ +abstract class PreferenceLifecycleContext(context: Context) : ContextWrapper(context) { + + /** Notifies that preference state is changed and update preference widget UI. */ + abstract fun notifyPreferenceChange(preference: PreferenceMetadata) + + /** + * Starts activity for result, see [android.app.Activity.startActivityForResult]. + * + * This API can be invoked by any preference, the caller must ensure the request code is unique + * on the preference screen. + */ + abstract fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt index ef3d372a4088..bcfef67032f1 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt @@ -101,9 +101,9 @@ interface PreferenceBinding { preference.setEntryValues(values) } } else if (preference is SeekBarPreference && this is RangeValue) { - preference.min = minValue - preference.max = maxValue - preference.seekBarIncrement = incrementStep + preference.min = getMinValue(context) + preference.max = getMaxValue(context) + preference.seekBarIncrement = getIncrementStep(context) } } } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt index d501f4fd654d..21621a8b39e1 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt @@ -17,6 +17,7 @@ package com.android.settingslib.preference import android.content.Context +import android.content.Intent import android.os.Bundle import android.util.Log import androidx.annotation.XmlRes @@ -41,7 +42,7 @@ open class PreferenceFragment : createPreferenceScreen(PreferenceScreenFactory(this)) override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? { - preferenceScreenBindingHelper?.close() + preferenceScreenBindingHelper?.onDestroy() preferenceScreenBindingHelper = null val context = factory.context @@ -66,9 +67,11 @@ open class PreferenceFragment : bindRecursively(it, preferenceBindingFactory, preferenceHierarchy) } ?: return null } + preferenceScreenBindingHelper = PreferenceScreenBindingHelper( context, + this, preferenceBindingFactory, preferenceScreen, preferenceHierarchy, @@ -87,12 +90,44 @@ open class PreferenceFragment : override fun getPreferenceScreenBindingKey(context: Context): String? = arguments?.getString(EXTRA_BINDING_SCREEN_KEY) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + preferenceScreenBindingHelper?.onCreate() + } + + override fun onStart() { + super.onStart() + preferenceScreenBindingHelper?.onStart() + } + + override fun onResume() { + super.onResume() + preferenceScreenBindingHelper?.onResume() + } + + override fun onPause() { + preferenceScreenBindingHelper?.onPause() + super.onPause() + } + + override fun onStop() { + preferenceScreenBindingHelper?.onStop() + super.onStop() + } + override fun onDestroy() { - preferenceScreenBindingHelper?.close() + preferenceScreenBindingHelper?.onDestroy() preferenceScreenBindingHelper = null super.onDestroy() } + @Suppress("DEPRECATION") + @Deprecated("Deprecated in Java") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + preferenceScreenBindingHelper?.onActivityResult(requestCode, resultCode, data) + } + protected fun getPreferenceKeysInHierarchy(): Set<String> = preferenceScreenBindingHelper?.getPreferences()?.map { it.key }?.toSet() ?: setOf() diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt index efa1faf6c485..d99d470cc4c0 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -17,6 +17,8 @@ package com.android.settingslib.preference import android.content.Context +import android.content.Intent +import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.preference.Preference @@ -29,6 +31,7 @@ import com.android.settingslib.datastore.KeyedObservable import com.android.settingslib.datastore.KeyedObserver import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceHierarchy +import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceScreenRegistry @@ -45,10 +48,11 @@ import java.util.concurrent.Executor */ class PreferenceScreenBindingHelper( context: Context, + fragment: PreferenceFragment, private val preferenceBindingFactory: PreferenceBindingFactory, private val preferenceScreen: PreferenceScreen, private val preferenceHierarchy: PreferenceHierarchy, -) : KeyedDataObservable<String>(), AutoCloseable { +) : KeyedDataObservable<String>() { private val handler = Handler(Looper.getMainLooper()) private val executor = @@ -58,8 +62,22 @@ class PreferenceScreenBindingHelper( } } + private val preferenceLifecycleContext = + object : PreferenceLifecycleContext(context) { + override fun notifyPreferenceChange(preference: PreferenceMetadata) = + notifyChange(preference.key, CHANGE_REASON_STATE) + + @Suppress("DEPRECATION") + override fun startActivityForResult( + intent: Intent, + requestCode: Int, + options: Bundle?, + ) = fragment.startActivityForResult(intent, requestCode, options) + } + private val preferences: ImmutableMap<String, PreferenceMetadata> private val dependencies: ImmutableMultimap<String, String> + private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider> private val storages = mutableSetOf<KeyedObservable<String>>() private val preferenceObserver: KeyedObserver<String?> @@ -71,16 +89,10 @@ class PreferenceScreenBindingHelper( } } - private val stateObserver = - object : PreferenceLifecycleProvider.PreferenceStateObserver { - override fun onPreferenceStateChanged(preference: PreferenceMetadata) { - notifyChange(preference.key, CHANGE_REASON_STATE) - } - } - init { val preferencesBuilder = ImmutableMap.builder<String, PreferenceMetadata>() val dependenciesBuilder = ImmutableMultimap.builder<String, String>() + val lifecycleAwarePreferences = mutableListOf<PreferenceLifecycleProvider>() fun PreferenceMetadata.addDependency(dependency: PreferenceMetadata) { dependenciesBuilder.put(key, dependency.key) } @@ -88,7 +100,7 @@ class PreferenceScreenBindingHelper( fun PreferenceMetadata.add() { preferencesBuilder.put(key, this) dependencyOfEnabledState(context)?.addDependency(this) - if (this is PreferenceLifecycleProvider) onAttach(context, stateObserver) + if (this is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(this) if (this is PersistentPreference<*>) storages.add(storage(context)) } @@ -106,6 +118,7 @@ class PreferenceScreenBindingHelper( preferenceHierarchy.addPreferences() this.preferences = preferencesBuilder.buildOrThrow() this.dependencies = dependenciesBuilder.build() + this.lifecycleAwarePreferences = lifecycleAwarePreferences.toTypedArray() preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) } addObserver(preferenceObserver, executor) @@ -137,13 +150,48 @@ class PreferenceScreenBindingHelper( fun getPreferences() = preferenceHierarchy.getAllPreferences() - override fun close() { - removeObserver(preferenceObserver) - val context = preferenceScreen.context - for (preference in preferences.values) { - if (preference is PreferenceLifecycleProvider) preference.onDetach(context) + fun onCreate() { + for (preference in lifecycleAwarePreferences) { + preference.onCreate(preferenceLifecycleContext) + } + } + + fun onStart() { + for (preference in lifecycleAwarePreferences) { + preference.onStart(preferenceLifecycleContext) + } + } + + fun onResume() { + for (preference in lifecycleAwarePreferences) { + preference.onResume(preferenceLifecycleContext) + } + } + + fun onPause() { + for (preference in lifecycleAwarePreferences) { + preference.onPause(preferenceLifecycleContext) + } + } + + fun onStop() { + for (preference in lifecycleAwarePreferences) { + preference.onStop(preferenceLifecycleContext) } + } + + fun onDestroy() { + removeObserver(preferenceObserver) for (storage in storages) storage.removeObserver(storageObserver) + for (preference in lifecycleAwarePreferences) { + preference.onDestroy(preferenceLifecycleContext) + } + } + + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + for (preference in lifecycleAwarePreferences) { + if (preference.onActivityResult(requestCode, resultCode, data)) break + } } companion object { diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml index 18696c627ec6..91a95a51fae2 100644 --- a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml +++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml @@ -16,6 +16,12 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:attr/colorControlHighlight"> - <item android:drawable="@drawable/audio_sharing_rounded_bg"/> + <item> + <shape android:shape="rectangle"> + <corners android:radius="4dp" /> + <solid android:color="?androidprv:attr/colorAccentPrimary" /> + </shape> + </item> </ripple>
\ No newline at end of file diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple_bottom.xml index 35517ea0ec11..cce8a75a3385 100644 --- a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml +++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple_bottom.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2023 The Android Open Source Project + ~ 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. @@ -15,10 +15,17 @@ ~ limitations under the License. --> -<shape - xmlns:android="http://schemas.android.com/apk/res/android" +<ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> - <corners android:radius="12dp" /> -</shape>
\ No newline at end of file + android:color="?android:attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <corners + android:bottomLeftRadius="12dp" + android:bottomRightRadius="12dp" + android:topLeftRadius="4dp" + android:topRightRadius="4dp" /> + <solid android:color="?androidprv:attr/colorAccentPrimary" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple_top.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple_top.xml new file mode 100644 index 000000000000..140419705dd5 --- /dev/null +++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple_top.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:color="?android:attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <corners + android:bottomLeftRadius="4dp" + android:bottomRightRadius="4dp" + android:topLeftRadius="12dp" + android:topRightRadius="12dp" /> + <solid android:color="?androidprv:attr/colorAccentPrimary" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 2f158c88305a..79c379996d8b 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktiveer die Verbeterde Konnektiwiteit-kenmerk."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Plaaslike terminaal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktiveer terminaalprogram wat plaaslike skermtoegang bied"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-ontwikkelingomgewing"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Laat loop Linux-terminaal op Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrolering"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Stel HDCP-kontrolering se gedrag"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Ontfouting"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade oorfoon"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Bedrade oudio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-oudio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Bedrade mikrofoon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 1040f377c6cd..5cde078af811 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"የተሻሻለ ተገናኝነት ባህሪውን ያነቃል።"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"አካባቢያዊ ተርሚናል"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"የአካባቢያዊ ሼል መዳረሻ የሚያቀርብ የተርሚናል መተግበሪያ አንቃ"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"የLinux ግንባታ አከባቢ"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android ላይ Linux ተርሚናል ያሂዱ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"የHDCP ምልከታ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"የHDCP መመልከቻ ጠባይ አዘጋጅ"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ስህተት በማስወገድ ላይ"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ባለገመድ የራስ ላይ ማዳመጫ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ግራ የሚያጋባ ኦዶዮ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ኦዲዮ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ባለገመድ ማይክሮፎን"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ማይክሮፎን"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ብሉቱዝ ማይክሮፎን"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"አብራ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 9e1db5fbf36d..09bfd83f7121 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"لتفعيل الميزة \"إمكانية اتصال محسّن\""</string> <string name="enable_terminal_title" msgid="3834790541986303654">"تطبيق طرفي محلي"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"تفعيل تطبيق طرفي يوفر إمكانية الدخول إلى واجهة النظام المحلية"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"بيئة تطوير نظام التشغيل Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"تشغيل محطة Linux الطرفية على Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"التحقق من HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"تعيين سلوك التحقق من HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"تصحيح الأخطاء"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"يجب إعادة تشغيل جهازك ليتم تطبيق هذا التغيير. يمكنك إعادة التشغيل الآن أو إلغاء التغيير."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"سماعات رأس سلكية"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"سمّاعة رأس سلكية"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"مكبر صوت USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ميكروفون سلكي"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ميكروفون USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ميكروفون يعمل بالبلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"مفعّلة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 76a39368f7fd..83cd2fe07942 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"উন্নত সংযোগ সুবিধাটো সক্ষম কৰে।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টাৰ্মিনেল"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শ্বেলৰ এক্সেছ দিয়া টাৰ্মিনেল এপ্ সক্ষম কৰক"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পৰীক্ষণ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP পৰীক্ষণ আচৰণ ছেট কৰক"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ডিবাগিং"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই সলনিটো কার্যকৰী হ’বলৈ আপোনাৰ ডিভাইচটো ৰিবুট কৰিবই লাগিব। এতিয়াই ৰিবুট কৰক অথবা বাতিল কৰক।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তাঁৰযুক্ত হেডফ’ন"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"তাঁৰযুক্ত অডিঅ’"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ইউএছবি অডিঅ\'"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"তাঁৰযুক্ত মাইক্ৰ’ফ’ন"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ইউএছবি মাইক্ৰ’ফ’ন"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ব্লুটুথ মাইক্ৰ’ফ’ন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"অন"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 2d9e1d9fb726..459cfaaae9c4 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux inkişaf mühiti"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android-də Linux terminalını işə salın"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanışı"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP yoxlanışı qaydası ayalansın"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Sazlama"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Naqilli qulaqlıq"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Simli audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Simli mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 01f0ff26eddc..f883c93fd15e 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Omogućava funkciju Poboljšano povezivanje."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući apl. terminala za pristup lokalnom komandnom okruženju"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux okruženje za programiranje"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Pokrenite Linux terminal na Android-u"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provera"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Podešavanje ponašanja HDCP provere"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Otklanjanje grešaka"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate da restartujete uređaj da bi se ova promena primenila. Restartujte ga odmah ili otkažite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Žičane slušalice"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Žičani mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index f710b1d76028..ca07c4def3a0 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Уключае функцыю \"Палепшанае падключэнне\"."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Лакальны тэрмінал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Уключэнне прыкладання тэрмінала, якое прапануе доступ да лакальнай абалонкі"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Асяроддзе распрацоўкі Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Запусціць тэрмінал Linux на прыладзе Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Праверка HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Усталяваць рэжым праверкі HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Адладка"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Перазагрузіце прыладу, каб прымяніць гэта змяненне. Перазагрузіце ці скасуйце."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Правадныя навушнікі"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Правадныя навушнікі"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аўдыяпрылада USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Правадны мікрафон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Мікрафон USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Мікрафон з Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Уключана"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index c5c4c914a919..7661a31d4dc2 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Активира функцията за подобрена свързаност."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локален терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Актив. на прил. за терминал с достъп до локалния команден ред"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Среда на програмиране на Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Стартиране на терминала на Linux под Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка с HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Проверка с HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Отстраняване на грешки"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да бъде приложена тази промяна, устройството ви трябва да бъде рестартирано. Рестартирайте сега или анулирайте."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Слушалки с кабел"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Слушалки с кабел"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аудиоустройство с USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Микрофон с кабел"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Микрофон с USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон с Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Включване"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index be6b9be9887c..f71a003c14c9 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"কানেক্টিভিটি ফিচার উন্নত করার বিষয়টি চালু করা হয়েছে।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টার্মিনাল"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শেল অ্যাক্সেসের প্রস্তাব করে এমন টার্মিনাল অ্যাপ্লিকেশন সক্ষম করুন"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পরীক্ষণ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP চেক করার আচরণ সেট করুন"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ডিবাগিং"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"এই পরিবর্তনটি প্রয়োগ করার জন্য আপনার ডিভাইসটি অবশ্যই রিবুট করতে হবে। এখনই রিবুট করুন বা বাতিল করুন।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"তারযুক্ত হেডফোন"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"তারযুক্ত অডিও"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB অডিও"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"তারযুক্ত মাইক্রোফোন"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB মাইক্রোফোন"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT মাইক্রোফোন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"চালু আছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f34343050579..b76b028ac4b9 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Omogućava funkciju Poboljšane povezivosti."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući terminalnu aplik. koja nudi pristup lok. kom. okruženju"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linuxovo okruženje za razvoj"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Pokreni Linux terminal na Androidu"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provjera"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Postavke HDCP provjere"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Otklanjanje grešaka"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Morate ponovo pokrenuti uređaj da se ova promjena primijeni. Ponovo pokrenite odmah ili otkažite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Žičani audio uređaj"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Žičani mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključi"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 3557b10c764b..2aa3ffa6d145 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Activa la funció de connectivitat millorada."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activa l\'aplicació de terminal que ofereix accés al shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Entorn de desenvolupament Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Executa el terminal de Linux a Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprovació d\'HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Defineix comprovació HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuració"</string> @@ -611,10 +613,10 @@ <string name="shared_data_title" msgid="1017034836800864953">"Dades compartides"</string> <string name="shared_data_summary" msgid="5516326713822885652">"Mostra i modifica les dades compartides"</string> <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"No hi ha dades compartides per a aquest usuari"</string> - <string name="shared_data_query_failure_text" msgid="3489828881998773687">"S\'ha produït un error en recollir les dades compartides. Torna-ho a provar."</string> + <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Hi ha hagut un error en recollir les dades compartides. Torna-ho a provar."</string> <string name="blob_id_text" msgid="8680078988996308061">"Identificador de dades compartides: <xliff:g id="BLOB_ID">%d</xliff:g>"</string> <string name="blob_expires_text" msgid="7882727111491739331">"Caduquen el dia <xliff:g id="DATE">%s</xliff:g>"</string> - <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"S\'ha produït un error en suprimir les dades compartides."</string> + <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Hi ha hagut un error en suprimir les dades compartides."</string> <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"No s\'ha adquirit cap arrendament d\'aquestes dades. Vols suprimir-les?"</string> <string name="accessor_info_title" msgid="8289823651512477787">"Aplicacions que comparteixen dades"</string> <string name="accessor_no_description_text" msgid="7510967452505591456">"L\'aplicació no ha proporcionat cap descripció."</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Has de reiniciar el teu dispositiu perquè s\'apliquin els canvis. Reinicia\'l ara o cancel·la."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculars amb cable"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Àudio amb cable"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Àudio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micròfon amb cable"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micròfon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micròfon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activa"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index ae47c4677da1..0a88338cf264 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktivuje funkci Lepší připojování."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Místní terminál"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivovat terminálovou aplikaci pro místní přístup k prostředí shell"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Vývojové prostředí Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Spustit na Androidu terminál Linux"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrola HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Nastavit chování kontroly HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Ladění"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aby se tato změna projevila, je třeba zařízení restartovat. Restartujte zařízení nebo zrušte akci."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelová sluchátka"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Zvuk přes kabel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Kabelový mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnout"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 82d702124a5a..983003bf1550 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktivér funktionen Enhanced Connectivity."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivér terminalappen, der giver lokal shell-adgang"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-udviklingsmiljø"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Kør Linux-terminal i Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrol"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Angiv HDCP-kontroladfærd"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Fejlretning"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Din enhed skal genstartes for at anvende denne ændring. Genstart nu, eller annuller."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Høretelefoner med ledning"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Lyd med ledning"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lydenhed"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon med ledning"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Til"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 00ae7e48fd72..1a5e15a5903f 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktiviert die Funktion \"Verbesserte Konnektivität\"."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokales Terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Terminal-App mit Zugriff auf lokale Shell aktivieren"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-Prüfung"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP-Prüfverhalten festlegen"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Damit diese Änderung übernommen wird, musst du dein Gerät neu starten. Du kannst es jetzt neu starten oder den Vorgang abbrechen."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kabelgebundene Kopfhörer"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Kabelgebundene Kopfhörer"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-Audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Kabelgebundenes Mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-Mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-Mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"An"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 7dbe27485491..c838f689528e 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Επιτρέπει τη λειτουργία Βελτιωμένης συνδεσιμότητας."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Τοπική τερματική εφαρμογή"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ενεργοπ.τερμ.εφαρμογής που προσφέρει πρόσβαση στο τοπικό κέλυφος"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Περιβάλλον ανάπτυξης Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Εκτέλεση τερματικού Linux σε Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Έλεγχος HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Ρύθμιση συμπεριφοράς ελέγχου HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Εντοπισμός σφαλμάτων"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Για να εφαρμοστεί αυτή η αλλαγή, θα πρέπει να επανεκκινήσετε τη συσκευή σας. Επανεκκίνηση τώρα ή ακύρωση."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Ενσύρματα ακουστικά"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Ενσύρματος ήχος"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Ήχος USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Ενσύρματο μικρόφωνο"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Μικρόφωνο USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Μικρόφωνο BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ενεργό"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index f29a02bc21e4..b4bf2055df0e 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Enables the enhanced connectivity feature."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux development environment"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Run Linux terminal on Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Set HDCP checking behaviour"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired microphone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 81489fe4cd65..af392b83cfb1 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Enables the Enhanced Connectivity feature."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux development environment"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Run Linux terminal on Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Set HDCP checking behavior"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index f29a02bc21e4..b4bf2055df0e 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Enables the enhanced connectivity feature."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux development environment"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Run Linux terminal on Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Set HDCP checking behaviour"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired microphone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index f29a02bc21e4..b4bf2055df0e 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Enables the enhanced connectivity feature."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux development environment"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Run Linux terminal on Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Set HDCP checking behaviour"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugging"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired headphones"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired microphone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 07b8d88a95d5..3c1a27822a3c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Habilita la función Conectividad mejorada."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Habilitar aplicac. de terminal que ofrece acceso al shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Entorno de desarrollo de Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Ejecuta la terminal de Linux en Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Configurar comportamiento de la comprobación HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuración"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Debes reiniciar el dispositivo para que se aplique el cambio. Reinícialo ahora o cancela la acción."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Auriculares con cable"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micrófono con cable"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activar"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 56355b1059ce..94502c5e0b3a 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Habilita la función de conectividad mejorada."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Habilitar aplicación de terminal que ofrece acceso a shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Entorno de desarrollo de Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Ejecuta un terminal de Linux en Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación de HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Establecer comprobación HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuración"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Es necesario reiniciar tu dispositivo para que se apliquen los cambios. Reinicia ahora o cancela la acción."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio con cable"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micrófono con cable"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activado"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 6582864e2e32..5f5a38c1464b 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Lubab täiustatud ühenduvuse funktsiooni."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Kohalik terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Luba kohalikku turvalist juurdepääsu pakkuv terminalirakendus"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linuxi arenduskeskkond"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Linuxi terminali käitamine Androidis"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrollimine"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP käitumise määramine"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Silumine"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Selle muudatuse rakendamiseks tuleb seade taaskäivitada. Taaskäivitage kohe või tühistage."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Juhtmega kõrvaklapid"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Juhtmega heli"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-heli"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Juhtmega mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Sees"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index f9ec6f715448..2c512b78b5ef 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Konexioak hobetzeko eginbidea gaitzen du."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Tokiko terminala"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Gaitu tokiko shell-sarbidea duen terminal-aplikazioa"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-eko garapen-ingurunea"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Exekutatu Linux-en terminala Android-en"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP egiaztapena"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Ezarri HDCP egiaztapen-portaera"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Arazketa"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Entzungailu kableduna"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio kableduna"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB bidezko audioa"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofono kableduna"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB bidezko mikrofonoa"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth bidezko mikrofonoa"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktibatu"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 9c704c5b89eb..1aeca86aed6c 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ویژگی «اتصال بهبودیافته» را فعال میکند."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ترمینال محلی"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"فعال کردن ترمینال برنامه کاربردی که دسترسی به برنامه محلی را پیشنهاد میکند"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"محیط توسعه نرمافزار Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"اجرا کردن پایانه Linux در Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"بررسی HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"تنظیم عملکرد بررسی HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"اشکالزدایی"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاه باید بازراهاندازی شود. یا اکنون بازراهاندازی کنید یا لغو کنید."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"هدفون سیمی"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"صدای سیمی"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"بلندگوی USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"میکروفون سیمی"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"میکروفون USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"میکروفون بلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"روشن"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 193ec7065086..53724a3b7d86 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ottaa käyttöön Parannetut yhteydet ‑ominaisuuden."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Paikallinen pääte"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ota käyttöön päätesov. joka mahdollistaa paikall. liittymäkäytön"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-kehitysympäristö"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Käynnistä Linux-pääte Androidilla"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-tarkistus"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Aseta HDCP-tarkistus"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Vianetsintä"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Laitteesi on käynnistettävä uudelleen, jotta muutos tulee voimaan. Käynnistä uudelleen nyt tai peru."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Langalliset kuulokkeet"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Langallinen audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Langallinen mikrofoni"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoni"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoni"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Päällä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index b527990c5221..30eba9eceb5a 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Active la fonctionnalité Connectivité améliorée."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activer l\'appli Terminal permettant l\'accès au shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Environnement de développement Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Exécuter le terminal Linux sur Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Vérification HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Configurer vérification HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Débogage"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Écouteurs filaires"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio filaire"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio par USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microphone filaire"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microphone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microphone BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activé"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index fb2bcf322edb..f4afc8701609 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Active la fonctionnalité Connectivité améliorée."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activer l\'application Terminal permettant l\'accès au shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Environnement de développement Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Exécuter le terminal Linux sur Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Vérification HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Config. vérification HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Débogage"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Vous devez redémarrer l\'appareil pour que cette modification soit appliquée. Redémarrez maintenant ou annulez l\'opération."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Casque filaire"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio filaire"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micro filaire"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micro USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micro Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Allumé"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index d6e9f3fb4210..be1fac816f7d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Activa a función de conectividade mellorada"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activa a aplicación terminal que ofrece acceso ao shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Contorno de programación Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Executar terminal de Linux en Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Definir comprobación HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuración"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auriculares con cable"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio con cable"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micrófono con cable"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index b804dd4ef8cb..f343ff4aa4c4 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"કનેક્ટિવિટીની વિસ્તૃત સુવિધા ચાલુ કરે છે."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"સ્થાનિક ટર્મિનલ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"સ્થાનિક શેલ અૅક્સેસની ઑફર કરતી ટર્મિનલ એપ્લિકેશનને સક્ષમ કરો"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP તપાસણી"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP તપાસણીની વર્તણૂક બદલો"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ડીબગિંગ"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"આ ફેરફારને લાગુ કરવા માટે તમારા ડિવાઇસને રીબૂટ કરવાની જરૂર છે. હમણાં જ રીબૂટ કરો કે રદ કરો."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"વાયરવાળો હૅડફોન"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"વાયર્ડ ઑડિયો"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ઑડિયો"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"વાયર્ડ માઇક્રોફોન"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB માઇક્રોફોન"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT માઇક્રોફોન"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ચાલુ"</string> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 7a22465f80e5..2173ad62404a 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -216,7 +216,7 @@ <string-array name="transition_animation_scale_entries"> <item msgid="3376676813923486384">"एनिमेशन बंद"</item> <item msgid="753422683600269114">"एनिमेशन स्केल .5x"</item> - <item msgid="3695427132155563489">"ऐनिमेशन स्केल 1x"</item> + <item msgid="3695427132155563489">"ऐनिमेशन स्केल 1 गुना"</item> <item msgid="9032615844198098981">"एनिमेशन स्केल 1.5x"</item> <item msgid="8473868962499332073">"एनिमेशन स्केल 2x"</item> <item msgid="4403482320438668316">"एनिमेशन स्केल 5x"</item> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 7c5e66ccaf03..352dba79565d 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"कनेक्टिविटी बेहतर बनाने की सुविधा को चालू करें"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux डेवलपमेंट एनवायरमेंट"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android पर Linux का टर्मिनल ऐप्लिकेशन चलाएं"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP जांच"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP जाँच व्यवहार सेट करें"</string> <string name="debug_debugging_category" msgid="535341063709248842">"डीबग करना"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"बदली गई सेटिंग को लागू करने के लिए, डिवाइस को रीस्टार्ट करना होगा. अपने डिवाइस को रीस्टार्ट करें या रद्द करें."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तार वाला हेडफ़ोन"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"तार वाला हेडफ़ोन"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"यूएसबी ऑडियो"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"तार वाला माइक्रोफ़ोन"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"यूएसबी माइक्रोफ़ोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ब्लूटूथ माइक्रोफ़ोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"चालू है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index fc474dd2957e..0df2a7a073e4 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Omogućuje značajku Poboljšana povezivost."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući aplikaciju terminala koja nudi pristup lokalnoj ovojnici"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linuxovo razvojno okruženje"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Pokrenite Linux terminal na Androidu"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provjera"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Postavke HDCP provjere"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Otklanjanje pogrešaka"</string> @@ -418,7 +420,7 @@ <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"Omogućeno na neograničeno vrijeme"</string> <string name="window_animation_scale_title" msgid="5236381298376812508">"Brzina animacije prozora"</string> <string name="transition_animation_scale_title" msgid="1278477690695439337">"Brzina animacije prijelaza"</string> - <string name="animator_duration_scale_title" msgid="7082913931326085176">"Razmjer duljine animatora"</string> + <string name="animator_duration_scale_title" msgid="7082913931326085176">"Brzina generiranja animacija"</string> <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulacija sekundarnih zaslona"</string> <string name="debug_applications_category" msgid="5394089406638954196">"Aplikacije"</string> <string name="immediately_destroy_activities" msgid="1826287490705167403">"Ukloni aktivnosti"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Uređaj se mora ponovno pokrenuti da bi se ta promjena primijenila. Ponovo pokrenite uređaj odmah ili odustanite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žičane slušalice"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Žičane slušalice"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB zvučnik"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Žičani mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 95b28928ab01..86a959a97cd0 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Bekapcsolja az Enhanced Connectivity funkciót."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Helyi végpont"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Végalkalmazás engedélyezése a helyi rendszerhéj eléréséhez"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux fejlesztői környezet"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Linux-terminál futtatása Androidon"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-ellenőrzés"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP-ellenőrzés beállítása"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Hibakeresés"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Az eszközt újra kell indítani, hogy a módosítás megtörténjen. Indítsa újra most, vagy vesse el a módosítást."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vezetékes fejhallgató"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Vezetékes audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hangeszköz"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Vezetékes mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Be"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 51190f3a2599..4c29740b552a 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Միացնում է «Տվյալների լավացված փոխանակում» գործառույթը։"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Տեղային տերմինալ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Միացնել տերմինալային հավելվածը, որն առաջարկում է մուտք տեղային խեցի"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Լինուքսի մշակման միջավայր"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Գործարկել Լինուքս տերմինալը Android-ում"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ստուգում"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP-ի ստուգման կարգը"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Վրիպազերծում"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Սարքն անհրաժեշտ է վերագործարկել, որպեսզի փոփոխությունը կիրառվի։ Վերագործարկեք հիմա կամ չեղարկեք փոփոխությունը։"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Լարով ականջակալ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Լարով միացվող աուդիո սարք"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB աուդիո"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Լարով միացվող խոսափող"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB խոսափող"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth խոսափող"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Միացնել"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 84280cf8a0de..3b265f8e9d38 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Mengaktifkan fitur Konektivitas Yang Disempurnakan."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal lokal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktifkan aplikasi terminal yang menawarkan akses kerangka lokal"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Lingkungan pengembangan Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Jalankan terminal Linux di Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Pemeriksaan HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Setel perilaku pemeriksaan HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Proses debug"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Perangkat Anda harus di-reboot agar perubahan ini diterapkan. Reboot sekarang atau batalkan."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Headphone berkabel"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio berkabel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon berkabel"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktif"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index daadae6010dd..d0184e112fc0 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Virkjar eiginleika aukinnar tengigetu."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Staðbundin skipanalína"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Virkja skipanalínuforrit sem leyfir staðbundinn skeljaraðgang"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Þróunarumhverfi Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Keyra Linux-útstöð í Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-athugun"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Velja virkni HDCP-ath."</string> <string name="debug_debugging_category" msgid="535341063709248842">"Villuleit"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Endurræsa þarf tækið til að þessi breyting taki gildi. Endurræstu núna eða hættu við."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Heyrnartól með snúru"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Heyrnartól með snúru"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hljóð"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Hljóðnemi með snúru"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-hljóðnemi"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-hljóðnemi"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Kveikt"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 36aa2b334cbe..773aa2659171 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Consente di attivare la funzionalità Connettività migliorata."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminale locale"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Abilita l\'app Terminale che offre l\'accesso alla shell locale"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambiente di sviluppo Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Esegui il terminale Linux su Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verifica HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Comportamento di verifica HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debug"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Per applicare questa modifica, devi riavviare il dispositivo. Riavvia ora o annulla."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Cuffie con cavo"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio con cavo"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microfono con cavo"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfono USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfono BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 942b99b68897..45c41dd4251b 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"הפעלה של תכונת הקישוריות המשופרת."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"מסוף מקומי"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"הפעלה של אפליקציית מסוף המציעה גישה מקומית למעטפת"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"סביבת פיתוח של Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"הפעלת טרמינל Linux ב-Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"בדיקת HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"הגדרת האופן של בדיקת HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ניפוי באגים"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"צריך להפעיל מחדש את המכשיר כדי להחיל את השינוי. יש להפעיל מחדש עכשיו או לבטל."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"אוזניות חוטיות"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"אודיו עם חיבור קווי"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"אודיו ב-USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"מיקרופון עם חיבור קווי"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"מיקרופון ב-USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"מיקרופון BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"פועלת"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 5924ab2a42ca..ae3f1970d206 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"接続強化機能を有効にします。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ローカルターミナル"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ローカルシェルアクセスを提供するターミナルアプリを有効にします"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 開発環境"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android で Linux ターミナルを実行する"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP チェック"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP チェック動作を設定"</string> <string name="debug_debugging_category" msgid="535341063709248842">"デバッグ"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"この変更を適用するには、デバイスの再起動が必要です。今すぐ再起動するか、キャンセルしてください。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線ヘッドフォン"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"有線オーディオ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB オーディオ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"有線マイク"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB マイク"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT マイク"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ON"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 5f3d54f2dc70..fe9dbb52cdd0 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ჩართავს კავშირის გაძლიერებული შესაძლებლობის ფუნქციას."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ადგილობრივი ტერმინალი"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ლოკალურ გარსზე წვდომის ტერმინალური აპლიკაციის ჩართვა"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-ის შემუშავების გარემო"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Linux ტერმინალის გაშვება Android-ზე"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP შემოწმება"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"დააყენე HDCP შემოწმების ქცევა"</string> <string name="debug_debugging_category" msgid="535341063709248842">"გამართვა"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ამ ცვლილების ასამოქმედებლად თქვენი მოწყობილობა უნდა გადაიტვირთოს. გადატვირთეთ ახლავე ან გააუქმეთ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"სადენიანი ყურსასმენი"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"სადენიანი აუდიო"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB აუდიო"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"სადენიანი მიკროფონი"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB მიკროფონი"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT მიკროფონი"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ჩართვა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index bb6e353ae321..1162e8f8a00f 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Жетілдірілген байланыс функциясын қосады."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Жергілікті терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Жергілікті шелл-код қол жетімділігін ұсынатын терминалды қолданбаны қосу"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux әзірлеуші ортасы"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android-та Linux терминалын іске қосыңыз."</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP тексерісі"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP (кең жолақты цифрлық контент қорғау) тексеру мүмкіндігін орнату"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Түзету"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бұл өзгеріс күшіне енуі үшін, құрылғыны қайта жүктеу керек. Қазір қайта жүктеңіз не бас тартыңыз."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Сымды құлақаспап"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Сымды аудио"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Сымды микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофоны"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Қосу"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 20ed723f4bc2..5a184c585ef5 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"បើកមុខងារការតភ្ជាប់ដែលបានធ្វើឱ្យប្រសើរឡើង។"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ស្ថានីយមូលដ្ឋាន"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"បើកកម្មវិធីស្ថានីយដែលផ្ដល់ការចូលសែលមូលដ្ឋាន"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"មជ្ឈដ្ឋានអភិវឌ្ឍន៍ Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"ដំណើរការទែមីណាល់ Linux នៅលើ Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"ពិនិត្យ HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"កំណត់ឥរិយាបថពិនិត្យ HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ការជួសជុល"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ត្រូវតែចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីឱ្យការផ្លាស់ប្ដូរនេះមានប្រសិទ្ធភាព។ ចាប់ផ្ដើមឡើងវិញឥឡូវនេះ ឬបោះបង់។"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"កាសមានខ្សែ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"សំឡេងប្រើខ្សែ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ឧបករណ៍បំពងសំឡេង USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"មីក្រូហ្វូនប្រើខ្សែ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"មីក្រូហ្វូន USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"មីក្រូហ្វូន BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"បើក"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 50a1234ca348..f34a43cfdd19 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ವರ್ಧಿತ ಸಂಪರ್ಕ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ಸ್ಥಳೀಯ ಟರ್ಮಿನಲ್"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ಸ್ಥಳೀಯ ಶೆಲ್ ಪ್ರವೇಶವನ್ನು ಒದಗಿಸುವ ಟರ್ಮಿನಲ್ ಆ್ಯಪ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ಡೆವಲಪ್ಮೆಂಟ್ ಎನ್ವಿರಾನ್ಮೆಂಟ್"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android ನಲ್ಲಿ Linux ಟರ್ಮಿನಲ್ ಅನ್ನು ರನ್ ಮಾಡಿ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ಪರೀಕ್ಷಿಸುವಿಕೆ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP ಪರಿಶೀಲನಾ ನಡವಳಿಕೆಯನ್ನು ಹೊಂದಿಸಿ"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ಡೀಬಗ್ ಮಾಡುವಿಕೆ"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ಈ ಬದಲಾವಣೆ ಅನ್ವಯವಾಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ರೀಬೂಟ್ ಮಾಡಬೇಕು. ಇದೀಗ ರೀಬೂಟ್ ಮಾಡಿ ಅಥವಾ ರದ್ದುಗೊಳಿಸಿ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ವೈಯರ್ ಹೊಂದಿರುವ ಹೆಡ್ಫೋನ್"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ವೈರ್ ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಆಡಿಯೋ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ಆಡಿಯೋ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ವೈರ್ ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಮೈಕ್ರೋಫೋನ್"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ಮೈಕ್ರೊಫೋನ್"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ಮೈಕ್ರೊಫೋನ್"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ಆನ್ ಆಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index f9949ce97a4c..d803404fb34a 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"향상된 연결 기능을 사용 설정합니다."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"로컬 터미널"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"로컬 셸 액세스를 제공하는 터미널 앱 사용"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 개발 환경"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android에서 Linux 터미널 실행"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 확인"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP 확인 동작 설정"</string> <string name="debug_debugging_category" msgid="535341063709248842">"디버깅"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"변경사항을 적용하려면 기기를 재부팅해야 합니다. 지금 재부팅하거나 취소하세요."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"유선 헤드폰"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"유선 오디오"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 오디오"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"유선 마이크"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 마이크"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"블루투스 마이크"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"사용"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 40bbb35bdec3..2fb036f8356c 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Жакшыртылган туташуу функциясын иштетет."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Жергиликтүү терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Жергиликтүү буйрук кабыгын сунуштаган терминалга уруксат берүү"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux иштеп чыгуу чөйрөсү"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android\'де Linux терминалын иштетүү"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP текшерүү"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP текшерүү тартиби"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Мүчүлүштүктөрдү аныктоо"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Бул өзгөрүү күчүнө кириши үчүн, түзмөктү өчүрүп күйгүзүңүз. Азыр же кийинчерээк өчүрүп күйгүзсөңүз болот."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Зымдуу гарнитура"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Зымдуу аудио түзмөк"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Зымдуу микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофону"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Күйгүзүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index b5f89225eec0..da631b6bc87b 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ເປີດນຳໃຊ້ຄຸນສົມບັດການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal ໃນໂຕເຄື່ອງ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ເປີດນຳໃຊ້ແອັບຯ Terminal ທີ່ໃຫ້ການເຂົ້າເຖິງ shell ໃນໂຕເຄື່ອງໄດ້"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"ການກວດສອບ HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"ຕັ້ງວິທີການກວດສອບ HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ການດີບັກ"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ຫູຟັງແບບມີສາຍ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ສຽງແບບມີສາຍ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ສຽງ USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ໄມໂຄຣໂຟນແບບມີສາຍ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ໄມໂຄຣໂຟນ USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ໄມໂຄຣໂຟນ BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ເປີດ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index fd23ecefecfa..0c235b8a6f35 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Įgalinti patobulinto ryšio funkciją."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Vietinis terminalas"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Įgal. terminalo progr., siūlančią prieigą prie viet. apvalkalo"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"„Linux“ kūrimo aplinka"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"„Linux“ terminalo paleidimas sistemoje „Android“"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP tikrinimas"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Nust. HDCP tikrin. elgs."</string> <string name="debug_debugging_category" msgid="535341063709248842">"Derinimas"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kad pakeitimas būtų pritaikytas, įrenginį reikia paleisti iš naujo. Dabar paleiskite iš naujo arba atšaukite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Laidinės ausinės"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Laidinis garso įrenginys"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB garsas"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Laidinis mikrofonas"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofonas"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"„Bluetooth“ mikrofonas"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Įjungta"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 4fbbbe9ec028..daaebc880ec9 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Tiek iespējota uzlabotās savienojamības funkcija."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Vietējā beigu lietotne"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Iespējot beigu lietotni, kurā piedāvāta vietējā čaulas piekļuve"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux izstrādes vide"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Palaist Linux termināli Android ierīcē"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP pārbaude"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP pārb. iestatīšana"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Atkļūdošana"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Lai šīs izmaiņas tiktu piemērotas, nepieciešama ierīces atkārtota palaišana. Atkārtoti palaidiet to tūlīt vai atceliet izmaiņas."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Vadu austiņas"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Vadu audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Vadu mikrofons"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofons"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofons"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ieslēgts"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index ea53b93dab15..f5e84a23aa07 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ја овозможува функцијата „Подобрена поврзливост“."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локален терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Овозможи апликација на терминал што овозможува локален пристап кон школка."</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Програмерска околина на Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Извршување Linux-терминал на Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка со HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Постави однесување на проверка на HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Отстранување грешки"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичени слушалки"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Жичени слушалки"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудио"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Жичен микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон со Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вклучено"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 43ac8622a159..7dd2073642f3 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി ഫീച്ചർ പ്രവർത്തനക്ഷമമാക്കുന്നു."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"പ്രാദേശിക ടെർമിനൽ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"പ്രാദേശിക ഷെൽ ആക്സസ് നൽകുന്ന ടെർമിനൽ അപ്ലിക്കേഷൻ പ്രവർത്തനക്ഷമമാക്കുക"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ഡെവലപ്പ്മെന്റ് എൻവയോൺമെന്റ്"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android-ൽ Linux ടെർമിനൽ പ്രവർത്തിപ്പിക്കുക"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP പരിശോധന"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP ചെക്കിംഗ്രീതി സജ്ജമാക്കുക"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ഡീബഗ്ഗിംഗ്"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ഈ മാറ്റം ബാധകമാകുന്നതിന് നിങ്ങളുടെ ഉപകരണം റീബൂട്ട് ചെയ്യേണ്ടതുണ്ട്. ഇപ്പോൾ റീബൂട്ട് ചെയ്യുകയോ റദ്ദാക്കുകയോ ചെയ്യുക."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"വയേർഡ് ഹെഡ്ഫോൺ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"വയേർഡ് ഓഡിയോ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ഓഡിയോ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"വയേർഡ് മൈക്രോഫോൺ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB മൈക്രോഫോൺ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT മൈക്രോഫോൺ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ഓണാണ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index d3b67c752e7c..615edff447b4 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Сайжруулсан холболтын онцлогийг идэвхжүүлдэг."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локал терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Локал суурьт хандалт хийх боломж олгодог терминалын апп-г идэвхжүүлэх"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP шалгах"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP шалгах авирыг тохируулах"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Дебаг"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Энэ өөрчлөлтийг хэрэгжүүлэхийн тулд таны төхөөрөмжийг дахин асаах ёстой. Одоо дахин асаах эсвэл цуцлана уу."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Утастай чихэвч"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Утастай аудио"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Утастай микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Асаах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 9d22c0060f9e..be9464c30241 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"वर्धित कनेक्टिव्हिटी वैशिष्ट्य सुरू करा."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानिक टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux विकास पर्यावरण"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android वर Linux टर्मिनल रन करा"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP तपासणी"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP तपासणी वर्तन सेट करा"</string> <string name="debug_debugging_category" msgid="535341063709248842">"डीबग करणे"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"हा बदल लागू करण्यासाठी तुमचे डिव्हाइस रीबूट करणे आवश्यक आहे. आता रीबूट करा किंवा रद्द करा."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"वायर्ड हेडफोन"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"वायर्ड ऑडिओ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ऑडिओ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"वायर्ड मायक्रोफोन"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB मायक्रोफोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT मायक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 22ff9dd5d41a..8f67d339fba0 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Mendayakan ciri Kesambungan Dipertingkat"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal setempat"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Dayakan apl terminal yang menawarkan akses shell tempatan"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Persekitaran pembangunan Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Terminal Run Linux pada Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Penyemakan HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Ttpkn tngkh laku smk HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Menyahpepijat"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Peranti anda mesti dibut semula supaya perubahan ini berlaku. But semula sekarang atau batalkan."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fon kepala berwayar"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio berwayar"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon berwayar"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Hidup"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 9a337128786d..9bb30c74c03e 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်သည့် ဝန်ဆောင်မှုကို ဖွင့်ပါ။"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"လိုကယ်တာမီနယ်"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"local shell အသုံးပြုခွင့်ကမ်းလှမ်းသော တာမင်နယ်အပလီကေးရှင်းဖွင့်ပါ"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ဆော့ဖ်ဝဲရေးမှု ပတ်ဝန်းကျင်"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android တွင် Linux တာမီနယ် လုပ်ဆောင်ရန်"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP စစ်ဆေးမှု"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP စစ်ဆေးပုံကို သတ်မှတ်မည်"</string> <string name="debug_debugging_category" msgid="535341063709248842">"အမှားရှာဖွေဖယ်ရှားခြင်း"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ဤအပြောင်းအလဲ ထည့်သွင်းရန် သင့်စက်ကို ပြန်လည်စတင်ရမည်။ ယခု ပြန်လည်စတင်ပါ သို့မဟုတ် ပယ်ဖျက်ပါ။"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ကြိုးတပ်နားကြပ်"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ကြိုးသွယ်ထားသည့် အသံ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB အသံ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ကြိုးသွယ်ထားသည့် မိုက်ခရိုဖုန်း"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB မိုက်ခရိုဖုန်း"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT မိုက်ခရိုဖုန်း"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ဖွင့်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 3b03d739673a..10c0e2105d26 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Slår på Forbedret tilkobling-funksjonen."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktiver terminalappen som gir lokal kommandolistetilgang"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-utviklingsmiljø"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Kjør Linux-terminal på Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontroll"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Angi HDPC-kontrolladferd"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Feilsøking"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten din må startes på nytt for at denne endringen skal tre i kraft. Start på nytt nå eller avbryt."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hodetelefoner med kabel"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Lyd med kabel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lyd"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon med kabel"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 84c8466abfaa..b26376179c62 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"यसले परिष्कृत जडानको सुविधा सक्षम पार्छ।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"स्थानीय सेल पहुँच प्रदान गर्ने टर्मिनल एप सक्षम गर्नुहोस्"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux डेभलप्मेन्ट इन्भायरमेन्ट"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android मा Linux टर्मिनल एप प्रयोग गर्नुहोस्"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP जाँच"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP जाँच व्यवहार सेट गर्नुहोस्"</string> <string name="debug_debugging_category" msgid="535341063709248842">"डिबग गरिँदै छ"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"यो परिवर्तन लागू गर्न तपाईंको यन्त्र अनिवार्य रूपमा रिबुट गर्नु पर्छ। अहिले रिबुट गर्नुहोस् वा रद्द गर्नुहोस्।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"तारयुक्त हेडफोन"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"तारयुक्त अडियो"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB अडियो"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"तारयुक्त माइक्रोफोन"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB माइक्रोफोन"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT माइक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"अन छ"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 83df29acd836..75d561762967 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Hiermee wordt de functie voor verbeterde connectiviteit aangezet."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokale terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Terminal-app aanzetten die lokale shell-toegang biedt"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-ontwikkelomgeving"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Linux-terminal uitvoeren op Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-controle"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP-controlegedrag instellen"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Foutopsporing"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aan"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Bedrade koptelefoon"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Bedrade audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Bedrade microfoon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-microfoon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-microfoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index d03c8f30502e..fb3a16695536 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି ଫିଚର୍ ସକ୍ଷମ କରିଥାଏ।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ସ୍ଥାନୀୟ ଟର୍ମିନାଲ୍"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ସ୍ଥାନୀୟ ଶେଲ୍କୁ ଆକସେସ୍ ଦେଉଥିବା ଟର୍ମିନଲ୍ ଆପ୍କୁ ସକ୍ଷମ କରନ୍ତୁ"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ଯାଞ୍ଚ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCPର ଯାଞ୍ଚ ଗତିବିଧି ସେଟ୍ କରନ୍ତୁ"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ଡିବଗ୍ କରୁଛି"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ କରନ୍ତୁ।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ତାରଯୁକ୍ତ ହେଡଫୋନ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ୱାୟାର୍ଡ ଅଡିଓ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ଅଡିଓ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ୱାୟାର୍ଡ ମାଇକ୍ରୋଫୋନ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ମାଇକ୍ରୋଫୋନ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ମାଇକ୍ରୋଫୋନ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 1d482076ca59..06a17a3d69a0 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ਸਥਾਨਕ ਟਰਮੀਨਲ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ਟਰਮੀਨਲ ਐਪ ਨੂੰ ਚਾਲੂ ਕਰੋ ਜੋ ਸਥਾਨਕ ਸ਼ੈਲ ਪਹੁੰਚ ਪੇਸ਼ਕਸ਼ ਕਰਦਾ ਹੈ"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ਜਾਂਚ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP ਜਾਂਚ ਵਿਵਹਾਰ ਸੈੱਟ ਕਰੋ"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ਡੀਬੱਗਿੰਗ"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ਇਸ ਤਬਦੀਲੀ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਰੀਬੂਟ ਕਰਨਾ ਲਾਜ਼ਮੀ ਹੈ। ਹੁਣੇ ਰੀਬੂਟ ਕਰੋ ਜਾਂ ਰੱਦ ਕਰੋ।"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"ਤਾਰ ਵਾਲੇ ਹੈੱਡਫ਼ੋਨ"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ਆਡੀਓ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ਤਾਰ ਵਾਲਾ ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ਚਾਲੂ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index e8492d33e39f..0a8ea16b0279 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Włącza funkcję lepszej obsługi połączeń."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal lokalny"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Włącz terminal, który umożliwia dostęp do powłoki lokalnej"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Środowisko programistyczne Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Uruchom terminal Linux na Androidzie"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Sprawdzanie HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Ustaw sprawdzanie HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Debugowanie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 20bd69a2e15e..c39648817af9 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ativa o recurso \"Conectividade melhorada\"."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar o app terminal que oferece acesso ao shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambiente de desenvolvimento do Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Executar terminal Linux no Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Config. a verificação HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuração"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Áudio com fio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microfone com fio"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 488dd5aa5652..be91222c38f2 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ativa a funcionalidade Conetividade melhorada."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar aplicação terminal que oferece acesso local à shell"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambiente de programação Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Executar terminal do Linux no Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Definir o comportamento da verificação HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuração"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Auscultadores com fios"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Áudio com fios"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microfone com fios"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ligado"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 20bd69a2e15e..c39648817af9 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ativa o recurso \"Conectividade melhorada\"."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar o app terminal que oferece acesso ao shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Ambiente de desenvolvimento do Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Executar terminal Linux no Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Config. a verificação HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depuração"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Fones de ouvido com fio"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Áudio com fio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microfone com fio"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index edd03560c176..4a0023fd6387 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Activează funcția Conectivitate îmbunătățită."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Aplicație terminal locală"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activează aplicația terminal care oferă acces la shell local"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Mediu de dezvoltare Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Rulează Linux terminal pe Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificare HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Configurează verif. HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Depanare"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pentru ca modificarea să se aplice, trebuie să repornești dispozitivul. Repornește-l acum sau anulează."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Căști cu fir"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio cu fir"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Microfon cu fir"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 55fbfa5711ba..1abe67a3f020 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Включить улучшенный обмен данными"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локальный терминальный доступ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Разрешить терминальный доступ к локальной оболочке"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Среда разработки Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Запустить терминал Linux в Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Порядок проверки HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Отладка"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Чтобы изменение вступило в силу, необходимо перезапустить устройство. Вы можете сделать это сейчас или позже."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Проводные наушники"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Проводное аудиоустройство"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудиоустройство"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Проводной микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вкл."</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index d48824a744b4..2764ed270497 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"වැඩිදියුණු කළ සබැඳුම් හැකියා විශේෂාංගය සබල කරයි."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"අභ්යන්තර අන්තය"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"දේශීය ෂෙල් ප්රවේශනය පිරිනමන ටර්මිනල් යෙදුම සබල කරන්න"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP පරික්ෂාව"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP පරික්ෂා හැසිරීම සකසන්න"</string> <string name="debug_debugging_category" msgid="535341063709248842">"නිදොස්කරණය"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"මෙම වෙනස යෙදීමට ඔබේ උපාංගය නැවත පණ ගැන්විය යුතුය. දැන් නැවත පණ ගන්වන්න හෝ අවලංගු කරන්න."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"රැහැන්ගත හෙඩ්ෆෝන්"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"රැහැන්ගත ශ්රව්ය"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ශ්රව්ය"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"රැහැන්ගත මයික්රෆෝනය"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB මයික්රෆෝනය"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT මයික්රෆෝනය"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ක්රියාත්මකයි"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index b80748296ad0..74c294b84db7 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Povoľuje funkciu Zlepšené možnosti pripojenia."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Miestny terminál"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Povoliť terminálovú apl. na miestny prístup k prostrediu shell"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Vývojové prostredie Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Spúšťanie terminálu systému Linux v Androide"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrola HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Nastaviť spôsob kontroly HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Ladenie"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Zmena sa prejaví až po reštarte zariadenia. Môžete ho teraz reštartovať alebo akciu zrušiť."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Slúchadlá s káblom"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Zvuk cez kábel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk cez USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Káblový mikrofón"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofón s rozhraním USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofón Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnúť"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index a4c0622f368c..0945cc042dc5 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Omogoči funkcijo Izboljšana povezljivost."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogočanje terminalske aplikacije za dostop do lokalne lupine"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Razvojno okolje Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Zagon terminala Linux v Androidu"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Preverjanje HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Nastavi preverjanje HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Odpravljanje napak"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Napravo je treba znova zagnati, da bo ta sprememba uveljavljena. Znova zaženite zdaj ali prekličite."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Žične slušalke"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Žične slušalke"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvok USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Žični mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vklop"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 19c42119d26e..e26d428ff99e 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -357,6 +357,10 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktivizon veçorinë e \"Lidhshmërisë së përmirësuar\"."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminali lokal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivizo aplikacionin terminal që ofron qasje në guaskën lokale"</string> + <!-- no translation found for enable_linux_terminal_title (5076044866895670637) --> + <skip /> + <!-- no translation found for enable_linux_terminal_summary (5893216510985145320) --> + <skip /> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrolli HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Cakto kontrollin e HDCP-së"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Korrigjimi i gabimeve"</string> @@ -686,11 +690,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kufje me tela"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Audio me tel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Pajisja audio me USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon me tel"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofoni me USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofoni me Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktive"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 1862910ff94a..d351a12bac21 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Омогућава функцију Побољшано повезивање."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локални терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Омогући апл. терминала за приступ локалном командном окружењу"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux окружење за програмирање"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Покрените Linux терминал на Android-у"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP провера"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Подешавање понашања HDCP провере"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Отклањање грешака"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Морате да рестартујете уређај да би се ова промена применила. Рестартујте га одмах или откажите."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Жичане слушалице"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Жичане слушалице"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Жичани микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Укључено"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 1ee80c5fb822..1c1117d39d5b 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Aktiverar funktionen Förbättrad anslutning."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivera en terminalapp som ger åtkomst till hyllor lokalt"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux-utvecklingsmiljö"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Kör Linux-terminalen på Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontroll"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Konfigurera HDCP-kontroll"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Felsökning"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Enheten måste startas om för att ändringen ska börja gälla. Starta om nu eller avbryt."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Hörlur med kabel"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Ljud med kabel"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-ljud"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Mikrofon med kabel"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index d33eafe24b69..820fcc877190 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Huwasha kipengele cha Muunganisho Ulioboreshwa."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Kituo cha karibu"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Washa programu ya mwisho inayotoa ufikiaji mkuu wa karibu"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Mazingira ya usanidi wa Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Tumia temino ya Linux kwenye Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Inakagua HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Weka HDCP ya kukagua tabia"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Utatuzi"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kipokea sauti cha kichwani chenye waya"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Kipokea sauti cha kichwani kinachotumia waya"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Sauti ya USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Maikrofoni inayotumia waya"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Maikrofoni ya USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Maikrofoni ya Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Umewashwa"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 2c0781d6837c..98412c1f41f4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"மேம்படுத்தப்பட்ட இணைப்புநிலை அம்சத்தை இயக்கும்."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"அக முனையம்"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"அக ஷெல் அணுகலை வழங்கும் இறுதிப் ஆப்ஸை இயக்கு"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux டெவெலப்மெண்ட் சூழல்"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Androidல் Linux டெர்மினலை இயக்கும்"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP சரிபார்ப்பு"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP சரிபார்க்கும் செயல்பாடுகளை அமை"</string> <string name="debug_debugging_category" msgid="535341063709248842">"பிழைதிருத்தம்"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"இந்த மாற்றங்கள் செயல்படுத்தப்பட உங்கள் சாதனத்தை மறுபடி தொடங்க வேண்டும். இப்போதே மறுபடி தொடங்கவும் அல்லது ரத்துசெய்யவும்."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"வயர்டு ஹெட்ஃபோன்"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"வயருள்ள ஹெட்ஃபோன்"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ஆடியோ"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"வயருள்ள மைக்ரோஃபோன்"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB மைக்ரோஃபோன்"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT மைக்ரோஃபோன்"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ஆன்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 145af84985b5..3770bb776627 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్ను అందించే టెర్మినల్ యాప్ను ప్రారంభించండి"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux డెవలప్మెంట్ ఎన్విరాన్మెంట్"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Androidలో Linux టెర్మినల్ను రన్ చేయండి"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP చెకింగ్"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string> <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ఈ మార్పును వర్తింపజేయాలంటే మీరు మీ పరికరాన్ని తప్పనిసరిగా రీబూట్ చేయాలి. ఇప్పుడే రీబూట్ చేయండి లేదా రద్దు చేయండి."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"వైర్ ఉన్న హెడ్ఫోన్"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"వైర్ ఉన్న ఆడియో"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ఆడియో"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"వైర్ ఉన్న మైక్రోఫోన్"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB మైక్రోఫోన్"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT మైక్రోఫోన్"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ఆన్లో ఉంది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index ae6585c51081..57b8f5914e71 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"เปิดใช้ฟีเจอร์การเชื่อมต่อที่ปรับปรุงแล้ว"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"เทอร์มินัลในตัวเครื่อง"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"เปิดใช้งานแอปเทอร์มินัลที่ให้การเข้าถึงเชลล์ในตัวเครื่อง"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"สภาพแวดล้อมในการพัฒนาซอฟต์แวร์ Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"เรียกใช้เทอร์มินัล Linux บน Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"การตรวจสอบ HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"ตั้งค่าการตรวจสอบ HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"การแก้ไขข้อบกพร่อง"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"คุณต้องรีบูตอุปกรณ์เพื่อให้การเปลี่ยนแปลงนี้มีผล รีบูตเลยหรือยกเลิก"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"หูฟังแบบใช้สาย"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"หูฟังแบบใช้สาย"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"เสียง USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"ไมโครโฟนแบบใช้สาย"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ไมโครโฟน USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ไมโครโฟน BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"เปิด"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 6e60e2b65932..e869e7f3bbb4 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Ine-enable ang feature na Pinagandang Pagkakonekta."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal na terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Paganahin ang terminal app na nag-aalok ng lokal na shell access"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Development environment ng Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Paganahin ang Linux terminal sa Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Pagsusuring HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP checking behavior"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Pagde-debug"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Dapat i-reboot ang iyong device para mailapat ang pagbabagong ito. Mag-reboot ngayon o kanselahin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Wired na headphone"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Wired na audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Wired na mikropono"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB na mikropono"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT na mikropono"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Naka-on"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 8d709b9f3916..188f5aa2ce8e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Gelişmiş Bağlantı özelliğini etkinleştirir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerel terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerel kabuk erişimi sunan terminal uygulamasını etkinleştir"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux geliştirme ortamı"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android\'de Linux terminali çalıştırın"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP denetimi"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP denetimini ayarla"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Hata ayıklama"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazınızın yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Kablolu kulaklık"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Kablolu ses"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ses cihazı"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Kablolu mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Açık"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 7b1086ca303f..b82db3a8a85f 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Вмикає функцію покращеного з\'єднання."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локальний термінал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Увімк. програму-термінал, що надає локальний доступ до оболонки"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Середовище Linux для розробки"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Запуск термінала Linux на Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Перевірка HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Порядок перевірки HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Налагодження"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Щоб застосувати ці зміни, потрібний перезапуск. Перезапустіть пристрій або скасуйте зміни."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Дротові навушники"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Дротовий аудіопристрій"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудіо"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Дротовий мікрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-мікрофон"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-мікрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Увімкнено"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index db00474f0750..43f03a20e0fc 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"بہتر کردہ کنیکٹوٹی کی خصوصیات کو فعال کرتا ہے۔"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"مقامی ٹرمینل"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"مقامی شیل رسائی پیش کرنے والی ٹرمینل ایپ فعال کریں"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux ڈیولپمنٹ ماحول"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android پر Linux ٹرمینل چلائیں"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP چیکنگ"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP چیکنگ برتاؤ سیٹ کریں"</string> <string name="debug_debugging_category" msgid="535341063709248842">"ڈیبگ کرنا"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"اس تبدیلی کو لاگو کرنے کے ليے آپ کے آلہ کو ریبوٹ کرنا ضروری ہے۔ ابھی ریبوٹ کریں یا منسوخ کریں۔"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"تار والا ہیڈ فون"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"تار والا ہیڈ فون"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB آڈیو"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"تار والا مائیکروفون"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB مائیکروفون"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT مائیکروفون"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"آن"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 833863e2eee7..685d6e63b082 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Kuchaytirilgan aloqa funksiyasini ishga tushiradi."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Mahalliy terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Mahalliy terminalga kirishga ruxsat beruvchi terminal ilovani faollashtirish"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux dasturlash muhiti"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Android orqali Linux terminalini ishga tushirish"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP tekshiruvi"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCPni tekshirish tartibi"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Nosozliklarni tuzatish"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar kuchga kirishi uchun qurilmani oʻchirib yoqing. Buni hozir yoki keyinroq bajarishingiz mumkin."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Simli quloqlik"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Simli audio"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Simli mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Yoniq"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index fba3a13864b5..7e98afddbe10 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Bật tính năng Kết nối nâng cao."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Dòng lệnh cục bộ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Bật ứng dụng dòng lệnh cung cấp quyền truy cập vỏ cục bộ"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Môi trường phát triển Linux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Chạy thiết bị đầu cuối Linux trên Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kiểm tra HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Đặt hành vi kiểm tra HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Gỡ lỗi"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bạn phải khởi động lại thiết bị để áp dụng sự thay đổi này. Hãy khởi động lại ngay hoặc hủy."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Tai nghe có dây"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Thiết bị âm thanh có dây"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Âm thanh qua cổng USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Micrô có dây"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrô USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrô BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Đang bật"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 29a4590878cf..0f3373e1142f 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"启用增强连接性功能。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本地终端"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"启用终端应用,以便在本地访问 Shell"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 开发环境"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"在 Android 上运行 Linux 终端"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 检查"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"设置 HDCP 检查行为"</string> <string name="debug_debugging_category" msgid="535341063709248842">"调试"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"设备必须重新启动才能应用此更改。您可以立即重新启动或取消。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有线耳机"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"有线音频"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音频"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"有线麦克风"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麦克风"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"蓝牙麦克风"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"开启"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 71fd6eec762b..d6ec1ca579e1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"啟用強化連線功能。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本機終端機"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"啟用可提供本機命令介面存取權的終端機應用程式"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 開發環境"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"在 Android 上執行 Linux 終端機"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 檢查"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"設定 HDCP 檢查行為"</string> <string name="debug_debugging_category" msgid="535341063709248842">"除錯"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"你的裝置必須重新開機,才能套用此變更。請立即重新開機或取消。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"有線音響"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"有線麥克風"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index b5700bcb31ea..08bf732652b9 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"啟用「加強型連線」功能。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本機終端機"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"啟用可提供本機命令介面存取權的終端機應用程式"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Linux 開發環境"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"在 Android 上執行 Linux 終端機"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 檢查"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"設定 HDCP 檢查行為"</string> <string name="debug_debugging_category" msgid="535341063709248842">"偵錯"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"裝置必須重新啟動才能套用這項變更。請立即重新啟動或取消變更。"</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"有線耳機"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"有線音訊"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"有線麥克風"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 2f822b35a704..ad4f04555d05 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -357,6 +357,8 @@ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Inika amandla isici Sokuxhumeka Okuthuthukisiwe."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Itheminali yasendaweni"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Nika amandla uhlelo lokusebenza letheminali olunikeza ukufinyelela kwasendaweni kwe-shell"</string> + <string name="enable_linux_terminal_title" msgid="5076044866895670637">"Indawo yokuthuthukiswa yeLinux"</string> + <string name="enable_linux_terminal_summary" msgid="5893216510985145320">"Sebenzisa itheminali yeLinux ku-Android"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Ihlola i-HDCP"</string> <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Hlela ukuhlola ukuziphatha kwe-HDCP"</string> <string name="debug_debugging_category" msgid="535341063709248842">"Ilungisa inkinga"</string> @@ -686,11 +688,9 @@ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string> <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string> <string name="media_transfer_wired_headphone_name" msgid="8698668536022665254">"Amahedifoni anentambo"</string> - <!-- no translation found for media_transfer_headphone_name (1157798825650178478) --> - <skip /> + <string name="media_transfer_headphone_name" msgid="1157798825650178478">"Umsindo onentambo"</string> <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Umsindo we-USB"</string> - <!-- no translation found for media_transfer_wired_device_mic_name (7115192790725088698) --> - <skip /> + <string name="media_transfer_wired_device_mic_name" msgid="7115192790725088698">"Imakrofoni enentambo"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Imakrofoni ye-USB"</string> <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Imakrofoni ye-BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vuliwe"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt index a33fcc6747b4..c16366e14560 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt @@ -279,7 +279,7 @@ class DeviceSettingServiceConnection( getService(intent, IDeviceSettingsProviderService.Stub::asInterface) .stateIn( coroutineScope.plus(backgroundCoroutineContext), - SharingStarted.WhileSubscribed(), + SharingStarted.WhileSubscribed(stopTimeoutMillis = SERVICE_CONNECTION_STOP_MILLIS), ServiceConnectionStatus.Connecting, ) }, @@ -370,5 +370,6 @@ class DeviceSettingServiceConnection( const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME" const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS" const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION" + const val SERVICE_CONNECTION_STOP_MILLIS = 1000L } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java index 63661f698f4e..4f315a2a2486 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InputRouteManager.java @@ -74,23 +74,7 @@ public final class InputRouteManager { new AudioDeviceCallback() { @Override public void onAudioDevicesAdded(@NonNull AudioDeviceInfo[] addedDevices) { - // Activate the last hot plugged valid input device, to match the output device - // behavior. - @AudioDeviceType int deviceTypeToActivate = mSelectedInputDeviceType; - for (AudioDeviceInfo info : addedDevices) { - if (InputMediaDevice.isSupportedInputDevice(info.getType())) { - deviceTypeToActivate = info.getType(); - } - } - - // Only activate if we find a different valid input device. e.g. if none of the - // addedDevices is supported input device, we don't need to activate anything. - if (mSelectedInputDeviceType != deviceTypeToActivate) { - mSelectedInputDeviceType = deviceTypeToActivate; - AudioDeviceAttributes deviceAttributes = - createInputDeviceAttributes(mSelectedInputDeviceType); - setPreferredDeviceForAllPresets(deviceAttributes); - } + applyDefaultSelectedTypeToAllPresets(); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index b94e9069da6b..481306a18f0e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -41,9 +41,9 @@ import android.media.RouteListingPreference; import android.os.SystemProperties; import android.util.Log; -import androidx.annotation.VisibleForTesting; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.settingslib.R; import com.android.settingslib.media.flags.Flags; @@ -98,7 +98,7 @@ public class PhoneMediaDevice extends MediaDevice { case TYPE_USB_ACCESSORY: name = inputRoutingEnabledAndIsDesktop(context) - ? context.getString(R.string.media_transfer_usb_audio_name) + ? routeInfo.getName() : context.getString(R.string.media_transfer_wired_headphone_name); break; case TYPE_DOCK: diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java index d808a25ebc04..782cee23fb42 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputRouteManagerTest.java @@ -24,7 +24,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -139,18 +138,6 @@ public class InputRouteManagerTest { /* address= */ ""); } - private AudioDeviceAttributes getUsbHeadsetDeviceAttributes() { - return new AudioDeviceAttributes( - AudioDeviceAttributes.ROLE_INPUT, - AudioDeviceInfo.TYPE_USB_HEADSET, - /* address= */ ""); - } - - private AudioDeviceAttributes getHdmiDeviceAttributes() { - return new AudioDeviceAttributes( - AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_HDMI, /* address= */ ""); - } - private void onPreferredDevicesForCapturePresetChanged(InputRouteManager inputRouteManager) { final List<AudioDeviceAttributes> audioDeviceAttributesList = new ArrayList<AudioDeviceAttributes>(); @@ -316,47 +303,21 @@ public class InputRouteManagerTest { } @Test - public void onAudioDevicesAdded_shouldActivateAddedDevice() { - final AudioManager audioManager = mock(AudioManager.class); - InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); - AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()}; - inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); - - // The only added wired headset will be activated. - for (@MediaRecorder.Source int preset : PRESETS) { - verify(audioManager, atLeast(1)) - .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes()); - } - } - - @Test - public void onAudioDevicesAdded_shouldActivateLastAddedDevice() { + public void onAudioDevicesAdded_shouldApplyDefaultSelectedDeviceToAllPresets() { final AudioManager audioManager = mock(AudioManager.class); - InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); - AudioDeviceInfo[] devices = {mockWiredHeadsetInfo(), mockUsbHeadsetInfo()}; - inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); - - // When adding multiple valid input devices, the last added device (usb headset in this - // case) will be activated. - for (@MediaRecorder.Source int preset : PRESETS) { - verify(audioManager, never()) - .setPreferredDeviceForCapturePreset(preset, getWiredHeadsetDeviceAttributes()); - verify(audioManager, atLeast(1)) - .setPreferredDeviceForCapturePreset(preset, getUsbHeadsetDeviceAttributes()); - } - } + AudioDeviceAttributes wiredHeadsetDeviceAttributes = getWiredHeadsetDeviceAttributes(); + when(audioManager.getDevicesForAttributes(INPUT_ATTRIBUTES)) + .thenReturn(Collections.singletonList(wiredHeadsetDeviceAttributes)); - @Test - public void onAudioDevicesAdded_doNotActivateInvalidAddedDevice() { - final AudioManager audioManager = mock(AudioManager.class); InputRouteManager inputRouteManager = new InputRouteManager(mContext, audioManager); - AudioDeviceInfo[] devices = {mockHdmiInfo()}; + AudioDeviceInfo[] devices = {mockWiredHeadsetInfo()}; inputRouteManager.mAudioDeviceCallback.onAudioDevicesAdded(devices); - // Do not activate since HDMI is not a valid input device. + // Called twice, one after initiation, the other after onAudioDevicesAdded call. + verify(audioManager, atLeast(2)).getDevicesForAttributes(INPUT_ATTRIBUTES); for (@MediaRecorder.Source int preset : PRESETS) { - verify(audioManager, never()) - .setPreferredDeviceForCapturePreset(preset, getHdmiDeviceAttributes()); + verify(audioManager, atLeast(2)) + .setPreferredDeviceForCapturePreset(preset, wiredHeadsetDeviceAttributes); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java index 1739c0e5e2bf..9f7f0c9cb38d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java @@ -17,7 +17,9 @@ package com.android.settingslib.media; import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; +import static android.media.MediaRoute2Info.TYPE_USB_ACCESSORY; import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; +import static android.media.MediaRoute2Info.TYPE_USB_HEADSET; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; @@ -120,28 +122,79 @@ public class PhoneMediaDeviceTest { @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) @Test - public void getName_returnCorrectName_desktop() { + public void getName_returnCorrectName_desktop_wiredHeadphones() { ShadowSystemProperties.override("ro.build.characteristics", "desktop"); when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES); + // Even if the MediaRoute2Info reports a name, the default string should still be displayed, + // since the MediaRoute2Info name is only used for USB devices. + when(mInfo.getName()).thenReturn("WIRED_HEADPHONES"); assertThat(mPhoneMediaDevice.getName()) .isEqualTo(mContext.getString(R.string.media_transfer_headphone_name)); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void getName_returnCorrectName_desktop_wiredHeadset() { + ShadowSystemProperties.override("ro.build.characteristics", "desktop"); when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADSET); + // Even if the MediaRoute2Info reports a name, the default string should still be displayed, + // since the MediaRoute2Info name is only used for USB devices. + when(mInfo.getName()).thenReturn("WIRED_HEADSET"); assertThat(mPhoneMediaDevice.getName()) .isEqualTo(mContext.getString(R.string.media_transfer_headphone_name)); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void getName_returnCorrectName_desktop_usbDevice() { + ShadowSystemProperties.override("ro.build.characteristics", "desktop"); when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE); + final String mediaRoute2InfoName = "USB-Audio - My Device"; + when(mInfo.getName()).thenReturn(mediaRoute2InfoName); - assertThat(mPhoneMediaDevice.getName()) - .isEqualTo(mContext.getString(R.string.media_transfer_usb_audio_name)); + assertThat(mPhoneMediaDevice.getName()).isEqualTo(mediaRoute2InfoName); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void getName_returnCorrectName_desktop_usbHeadset() { + ShadowSystemProperties.override("ro.build.characteristics", "desktop"); + + when(mInfo.getType()).thenReturn(TYPE_USB_HEADSET); + final String mediaRoute2InfoName = "USB-Audio - My Headset"; + when(mInfo.getName()).thenReturn(mediaRoute2InfoName); + + assertThat(mPhoneMediaDevice.getName()).isEqualTo(mediaRoute2InfoName); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void getName_returnCorrectName_desktop_usbAccessory() { + ShadowSystemProperties.override("ro.build.characteristics", "desktop"); + + when(mInfo.getType()).thenReturn(TYPE_USB_ACCESSORY); + final String mediaRoute2InfoName = "USB-Audio - My Accessory"; + when(mInfo.getName()).thenReturn(mediaRoute2InfoName); + + assertThat(mPhoneMediaDevice.getName()).isEqualTo(mediaRoute2InfoName); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void getName_returnCorrectName_desktop_builtinSpeaker() { + ShadowSystemProperties.override("ro.build.characteristics", "desktop"); when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER); + // Even if the MediaRoute2Info reports a name, the default string should still be displayed, + // since the MediaRoute2Info name is only used for USB devices. + when(mInfo.getName()).thenReturn("Phone"); - assertThat(mPhoneMediaDevice.getName()) - .isEqualTo(getMediaTransferThisDeviceName(mContext)); + assertThat(mPhoneMediaDevice.getName()).isEqualTo(getMediaTransferThisDeviceName(mContext)); } @EnableFlags(Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a8af43f5cb11..603a91195d04 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2479,10 +2479,10 @@ public class SettingsProvider extends ContentProvider { final long identity = Binder.clearCallingIdentity(); try { int currentUser = ActivityManager.getCurrentUser(); - if (callingUser == currentUser) { - // enforce the deny list only if the caller is not current user. Currently only auto - // uses background visible user, and auto doesn't support profiles so profiles of - // current users is not checked here. + if (callingUser == currentUser || callingUser == UserHandle.USER_SYSTEM) { + // enforce the deny list only if the caller is not current user or not a system + // user. Currently only auto uses background visible user, and auto doesn't + // support profiles so profiles of current users is not checked here. return; } } finally { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index d39b5645109d..1659c9eb67f2 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -903,8 +903,8 @@ public class SettingsBackupTest { Settings.System.EGG_MODE, // I am the lolrus Settings.System.END_BUTTON_BEHAVIOR, // bug? Settings.System.DEFAULT_DEVICE_FONT_SCALE, // Non configurable - Settings.System - .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, + Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, + Settings.System.INPUT_GAIN_INDEX_SETTINGS, // candidate for backup? Settings.System.LOCKSCREEN_DISABLED, // ? Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup? diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS index 177f86b08864..8feefa5d892f 100644 --- a/packages/Shell/OWNERS +++ b/packages/Shell/OWNERS @@ -1,5 +1,6 @@ set noparent +ronish@google.com jsharkey@android.com felipeal@google.com nandana@google.com diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index bcfd8f620f9c..b4d81d6937ed 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -30,7 +30,6 @@ import android.accounts.AccountManager; import android.annotation.MainThread; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.app.ActivityThread; import android.app.AlertDialog; import android.app.Notification; import android.app.Notification.Action; @@ -90,10 +89,10 @@ import com.android.internal.app.ChooserActivity; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import libcore.io.Streams; - import com.google.android.collect.Lists; +import libcore.io.Streams; + import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; @@ -117,7 +116,9 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -171,8 +172,20 @@ public class BugreportProgressService extends Service { static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT"; static final String EXTRA_INFO = "android.intent.extra.INFO"; - static final String EXTRA_EXTRA_ATTACHMENT_URI = - "android.intent.extra.EXTRA_ATTACHMENT_URI"; + static final String EXTRA_EXTRA_ATTACHMENT_URIS = + "android.intent.extra.EXTRA_ATTACHMENT_URIS"; + + private static final ThreadFactory sBugreportManagerCallbackThreadFactory = + new ThreadFactory() { + private static final ThreadFactory mFactory = Executors.defaultThreadFactory(); + + @Override + public Thread newThread(Runnable r) { + Thread thread = mFactory.newThread(r); + thread.setName("BRMgrCallbackThread"); + return thread; + } + }; private static final int MSG_SERVICE_COMMAND = 1; private static final int MSG_DELAYED_SCREENSHOT = 2; @@ -277,6 +290,7 @@ public class BugreportProgressService extends Service { private boolean mIsWatch; private boolean mIsTv; + private ExecutorService mBugreportSingleThreadExecutor; @Override public void onCreate() { @@ -307,6 +321,8 @@ public class BugreportProgressService extends Service { isTv(this) ? NotificationManager.IMPORTANCE_DEFAULT : NotificationManager.IMPORTANCE_LOW)); mBugreportManager = mContext.getSystemService(BugreportManager.class); + mBugreportSingleThreadExecutor = Executors.newSingleThreadExecutor( + sBugreportManagerCallbackThreadFactory); } @Override @@ -337,6 +353,7 @@ public class BugreportProgressService extends Service { public void onDestroy() { mServiceHandler.getLooper().quit(); mScreenshotHandler.getLooper().quit(); + mBugreportSingleThreadExecutor.close(); super.onDestroy(); } @@ -682,10 +699,11 @@ public class BugreportProgressService extends Service { long nonce = intent.getLongExtra(EXTRA_BUGREPORT_NONCE, 0); String baseName = getBugreportBaseName(bugreportType); String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); - Uri extraAttachment = intent.getParcelableExtra(EXTRA_EXTRA_ATTACHMENT_URI, Uri.class); + List<Uri> extraAttachments = + intent.getParcelableArrayListExtra(EXTRA_EXTRA_ATTACHMENT_URIS, Uri.class); BugreportInfo info = new BugreportInfo(mContext, baseName, name, shareTitle, - shareDescription, bugreportType, mBugreportsDir, nonce, extraAttachment); + shareDescription, bugreportType, mBugreportsDir, nonce, extraAttachments); synchronized (mLock) { if (info.bugreportFile.exists()) { Log.e(TAG, "Failed to start bugreport generation, the requested bugreport file " @@ -713,8 +731,6 @@ public class BugreportProgressService extends Service { } } - final Executor executor = ActivityThread.currentActivityThread().getExecutor(); - Log.i(TAG, "bugreport type = " + bugreportType + " bugreport file fd: " + bugreportFd + " screenshot file fd: " + screenshotFd); @@ -723,7 +739,8 @@ public class BugreportProgressService extends Service { try { synchronized (mLock) { mBugreportManager.startBugreport(bugreportFd, screenshotFd, - new BugreportParams(bugreportType), executor, bugreportCallback); + new BugreportParams(bugreportType), mBugreportSingleThreadExecutor, + bugreportCallback); bugreportCallback.trackInfoWithIdLocked(); } } catch (RuntimeException e) { @@ -1233,9 +1250,13 @@ public class BugreportProgressService extends Service { clipData.addItem(new ClipData.Item(null, null, null, screenshotUri)); attachments.add(screenshotUri); } - if (info.extraAttachment != null) { - clipData.addItem(new ClipData.Item(null, null, null, info.extraAttachment)); - attachments.add(info.extraAttachment); + if (info.extraAttachments != null) { + info.extraAttachments.forEach(it -> { + if (it != null) { + clipData.addItem(new ClipData.Item(null, null, null, it)); + attachments.add(it); + } + }); } intent.setClipData(clipData); intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); @@ -2096,7 +2117,7 @@ public class BugreportProgressService extends Service { final long nonce; @Nullable - public Uri extraAttachment = null; + public List<Uri> extraAttachments = null; private final Object mLock = new Object(); @@ -2106,7 +2127,7 @@ public class BugreportProgressService extends Service { BugreportInfo(Context context, String baseName, String name, @Nullable String shareTitle, @Nullable String shareDescription, @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce, - @Nullable Uri extraAttachment) { + @Nullable List<Uri> extraAttachments) { this.context = context; this.name = this.initialName = name; this.shareTitle = shareTitle == null ? "" : shareTitle; @@ -2115,7 +2136,7 @@ public class BugreportProgressService extends Service { this.nonce = nonce; this.baseName = baseName; this.bugreportFile = new File(bugreportsDir, getFileName(this, ".zip")); - this.extraAttachment = extraAttachment; + this.extraAttachments = extraAttachments; } void createBugreportFile() { diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 5f90b39808f8..9265ceb1c052 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -106,6 +106,7 @@ filegroup { srcs: [ "tests/src/**/systemui/ExpandHelperTest.java", "tests/src/**/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java", + "tests/src/**/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java", "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java", "tests/src/**/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java", "tests/src/**/systemui/screenshot/appclips/AppClipsActivityTest.java", @@ -269,6 +270,42 @@ filegroup { "tests/src/**/systemui/volume/VolumeDialogImplTest.java", "tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java", "tests/src/**/systemui/wallet/ui/WalletScreenControllerTest.java", + "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt", + "tests/src/**/systemui/biometrics/UdfpsControllerOverlayTest.kt", + // TODO(b/322324387): Fails to start due to missing ScreenshotActivity + "tests/src/**/systemui/bouncer/ui/composable/BouncerContentTest.kt", + "tests/src/**/systemui/bouncer/ui/composable/PatternBouncerTest.kt", + "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java", + "tests/src/**/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt", + "tests/src/**/systemui/communal/data/db/CommunalWidgetDaoTest.kt", + "tests/src/**/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt", + "tests/src/**/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt", + "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt", + "tests/src/**/systemui/lifecycle/ActivatableTest.kt", + "tests/src/**/systemui/lifecycle/HydratorTest.kt", + "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java", + "tests/src/**/systemui/qs/QSImplTest.java", + "tests/src/**/systemui/qs/panels/ui/compose/DragAndDropTest.kt", + "tests/src/**/systemui/qs/panels/ui/compose/ResizingTest.kt", + "tests/src/**/systemui/screenshot/ActionExecutorTest.kt", + "tests/src/**/systemui/screenshot/ScreenshotDetectionControllerTest.kt", + "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt", + "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java", + "tests/src/**/systemui/accessibility/floatingmenu/PositionTest.java", + "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", + "tests/src/**/systemui/screenshot/scroll/ScrollCaptureControllerTest.java", + "tests/src/**/systemui/lifecycle/SysuiViewModelTest.kt", + "tests/src/**/systemui/flags/FakeFeatureFlags.kt", + "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", + "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java", + "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java", + "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt", + "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java", + "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java", + "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt", + "tests/src/**/systemui/toast/ToastUITest.java", + "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt", + "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt", ], } @@ -276,42 +313,23 @@ filegroup { filegroup { name: "SystemUI-tests-broken-robofiles-compile", srcs: [ + "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java", + "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt", + "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java", + "tests/src/**/systemui/doze/DozeScreenStateTest.java", + "tests/src/**/keyguard/CarrierTextManagerTest.java", + "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt", + "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt", "tests/src/**/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt", - "tests/src/**/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt", - "tests/src/**/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt", "tests/src/**/systemui/controls/management/ControlsFavoritingActivityTest.kt", "tests/src/**/systemui/controls/management/ControlsProviderSelectorActivityTest.kt", - "tests/src/**/systemui/controls/start/ControlsStartableTest.kt", - "tests/src/**/systemui/haptics/slider/SliderStateTrackerTest.kt", - "tests/src/**/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt", - "tests/src/**/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt", - "tests/src/**/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt", - "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt", - "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt", - "tests/src/**/systemui/keyguard/ResourceTrimmerTest.kt", - "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt", "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt", - "tests/src/**/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt", - "tests/src/**/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt", + "tests/src/**/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt", "tests/src/**/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt", "tests/src/**/systemui/qs/tileimpl/QSTileViewImplTest.kt", - "tests/src/**/systemui/qs/tiles/DeviceControlsTileTest.kt", - "tests/src/**/systemui/screenshot/ActionExecutorTest.kt", - "tests/src/**/systemui/screenshot/ActionIntentCreatorTest.kt", - "tests/src/**/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt", - "tests/src/**/systemui/screenshot/TakeScreenshotServiceTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt", - "tests/src/**/systemui/statusbar/notification/icon/IconManagerTest.kt", - "tests/src/**/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt", - "tests/src/**/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt", - "tests/src/**/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt", - "tests/src/**/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt", - "tests/src/**/systemui/statusbar/policy/FlashlightControllerImplTest.kt", - "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt", - "tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt", + "tests/src/**/systemui/statusbar/policy/BatteryStateNotifierTest.kt", "tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt", "tests/src/**/keyguard/ClockEventControllerTest.kt", - "tests/src/**/systemui/animation/TransitionAnimatorTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt", @@ -319,9 +337,6 @@ filegroup { "tests/src/**/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt", "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt", - // TODO(b/322324387): Fails to start due to missing ScreenshotActivity - "tests/src/**/systemui/bouncer/ui/composable/BouncerContentTest.kt", - "tests/src/**/systemui/bouncer/ui/composable/PatternBouncerTest.kt", "tests/src/**/systemui/broadcast/UserBroadcastDispatcherTest.kt", "tests/src/**/systemui/charging/WiredChargingRippleControllerTest.kt", "tests/src/**/systemui/clipboardoverlay/ClipboardModelTest.kt", @@ -348,8 +363,6 @@ filegroup { "tests/src/**/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt", "tests/src/**/systemui/navigationbar/gestural/BackPanelControllerTest.kt", "tests/src/**/systemui/notetask/NoteTaskControllerTest.kt", - "tests/src/**/systemui/notetask/NoteTaskInitializerTest.kt", - "tests/src/**/systemui/power/domain/interactor/PowerInteractorTest.kt", "tests/src/**/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt", "tests/src/**/systemui/privacy/PrivacyItemControllerTest.kt", "tests/src/**/systemui/qs/external/CustomTileStatePersisterTest.kt", @@ -376,7 +389,6 @@ filegroup { "tests/src/**/systemui/statusbar/LightRevealScrimTest.kt", "tests/src/**/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt", "tests/src/**/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt", - "tests/src/**/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt", "tests/src/**/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt", "tests/src/**/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt", "tests/src/**/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt", @@ -406,8 +418,6 @@ filegroup { "tests/src/**/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt", "tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt", "tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt", - "tests/src/**/keyguard/KeyguardUpdateMonitorTest.java", - "tests/src/**/keyguard/CarrierTextManagerTest.java", "tests/src/**/systemui/ScreenDecorationsTest.java", "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt", "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt", @@ -431,33 +441,17 @@ filegroup { "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java", "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java", "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt", - "tests/src/**/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt", "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt", "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java", "tests/src/**/systemui/wmshell/BubblesTest.java", - "tests/src/**/systemui/biometrics/AuthRippleControllerTest.kt", - "tests/src/**/keyguard/KeyguardAbsKeyInputViewControllerTest.java", - "tests/src/**/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java", - "tests/src/**/systemui/clipboardoverlay/ClipboardListenerTest.java", - "tests/src/**/systemui/doze/DozeScreenStateTest.java", - "tests/src/**/systemui/keyguard/WorkLockActivityControllerTest.java", - "tests/src/**/systemui/media/dialog/MediaSwitchingControllerTest.java", - "tests/src/**/systemui/navigationbar/views/NavigationBarTest.java", - "tests/src/**/systemui/power/PowerNotificationWarningsTest.java", - "tests/src/**/systemui/qs/QSFooterViewControllerTest.java", - "tests/src/**/systemui/qs/QSImplTest.java", - "tests/src/**/systemui/qs/tiles/QuickAccessWalletTileTest.java", - "tests/src/**/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerBaseTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerDataTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerSignalTest.java", - "tests/src/**/systemui/statusbar/connectivity/NetworkControllerWifiTest.java", - "tests/src/**/systemui/statusbar/KeyguardIndicationControllerTest.java", - "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java", - "tests/src/**/systemui/statusbar/phone/ScrimControllerTest.java", - "tests/src/**/systemui/statusbar/policy/RotationLockControllerImplTest.java", - "tests/src/**/systemui/toast/ToastUITest.java", + "tests/src/**/systemui/power/PowerUITest.java", + "tests/src/**/systemui/qs/QSSecurityFooterTest.java", + "tests/src/**/systemui/qs/tileimpl/QSTileImplTest.java", + "tests/src/**/systemui/shared/plugins/PluginActionManagerTest.java", + "tests/src/**/systemui/statusbar/CommandQueueTest.java", + "tests/src/**/systemui/statusbar/connectivity/CallbackHandlerTest.java", + "tests/src/**/systemui/statusbar/policy/SecurityControllerTest.java", + "tests/src/**/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt", ], visibility: ["//visibility:private"], } @@ -885,6 +879,7 @@ android_robolectric_test { "android.test.base.impl", "android.test.mock.impl", "truth", + "androidx.test.ext.truth", ], upstream: true, @@ -922,6 +917,7 @@ android_robolectric_test { "android.test.base.impl", "android.test.mock.impl", "truth", + "androidx.test.ext.truth", ], upstream: true, diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml index 9d9b05a5e3cb..f9e548b48065 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml @@ -1,8 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menü für Bedienungshilfen"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menü „Bedienungshilfen“"</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistant"</string> <string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Einstellungen für Bedienungshilfen"</string> @@ -20,7 +20,7 @@ <string name="brightness_down_label" msgid="7115662941913272072">"Helligkeit verringern"</string> <string name="previous_button_content_description" msgid="840869171117765966">"Zum vorherigen Bildschirm"</string> <string name="next_button_content_description" msgid="6810058269847364406">"Zum nächsten Bildschirm"</string> - <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="accessibility_menu_summary" msgid="340071398148208130">"Gerät mit großem Menü steuern"</string> <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Einstellungen für das Menü „Bedienungshilfen“"</string> <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Große Schaltflächen"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-km/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-km/strings.xml index e091dd99a76d..02c091e42a29 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-km/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-km/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"ម៉ឺនុយភាពងាយស្រួល"</string> <string name="accessibility_menu_intro" msgid="3164193281544042394">"ម៉ឺនុយភាពងាយស្រួលផ្ដល់ម៉ឺនុយធំនៅលើអេក្រង់ ដើម្បីគ្រប់គ្រងឧបករណ៍របស់អ្នក។ អ្នកអាចចាក់សោឧបករណ៍របស់អ្នក គ្រប់គ្រងកម្រិតសំឡេងនិងពន្លឺ ថតរូបអេក្រង់ និងអ្វីៗច្រើនទៀត។"</string> - <string name="assistant_label" msgid="6796392082252272356">"ជំនួយការ"</string> + <string name="assistant_label" msgid="6796392082252272356">"Google Assistant"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistant"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"ការកំណត់ភាពងាយស្រួល"</string> <string name="power_label" msgid="7699720321491287839">"ថាមពល"</string> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a984b7d43bb6..44aa0b2d4a41 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -33,6 +33,13 @@ flag { } flag { + name: "notifications_redesign_footer_view" + namespace: "systemui" + description: "Notifications Redesign: Update the look of the notifications footer." + bug: "375010573" +} + +flag { name: "notification_row_content_binder_refactor" namespace: "systemui" description: "Convert the NotificationContentInflater to Kotlin and restructure it to support modern views" @@ -81,6 +88,16 @@ flag { } flag { + name: "notifications_footer_visibility_fix" + namespace: "systemui" + description: "Fixes a bug where the footer would briefly appear when dismissing a HUN" + bug: "356552869" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "notifications_footer_view_refactor" namespace: "systemui" description: "Enables the refactored version of the footer view in the notification shade " @@ -129,6 +146,13 @@ flag { } flag { + name: "notifications_dismiss_pruned_summaries" + namespace: "systemui" + description: "NotifCollection.dismissNotifications will now dismiss summaries that are pruned from the shade." + bug: "355967751" +} + +flag { name: "notification_transparent_header_fix" namespace: "systemui" description: "fix the transparent group header issue for async header inflation." @@ -411,9 +435,9 @@ flag { } flag { - name: "status_bar_notification_chips" + name: "status_bar_ron_chips" namespace: "systemui" - description: "Show promoted ongoing notifications as chips in the status bar" + description: "Show rich ongoing notifications as chips in the status bar" bug: "361346412" } @@ -1529,6 +1553,23 @@ flag { } flag { + name: "shade_window_goes_around" + namespace: "systemui" + description: "Enables the shade window to move between displays" + bug: "362719719" +} + +flag { + name: "transition_race_condition" + namespace: "systemui" + description: "Thread-safe keyguard transitions" + bug: "358533338" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "media_projection_request_attribution_fix" namespace: "systemui" description: "Ensure MediaProjection consent requests are properly attributed" @@ -1559,6 +1600,16 @@ flag { } flag { + name: "hide_ringer_button_in_single_volume_mode" + namespace: "systemui" + description: "When the device is in single volume mode, hide the ringer button because it doesn't work" + bug: "374870615" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "qs_tile_detailed_view" namespace: "systemui" description: "Enables the tile detailed view UI." diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp index d9230ec67461..7d7302309af2 100644 --- a/packages/SystemUI/animation/lib/Android.bp +++ b/packages/SystemUI/animation/lib/Android.bp @@ -48,6 +48,19 @@ java_library { } filegroup { + name: "PlatformAnimationLib-client-srcs", + srcs: [ + "src/com/android/systemui/animation/OriginRemoteTransition.java", + "src/com/android/systemui/animation/OriginTransitionSession.java", + "src/com/android/systemui/animation/SurfaceUIComponent.java", + "src/com/android/systemui/animation/Transactions.java", + "src/com/android/systemui/animation/UIComponent.java", + "src/com/android/systemui/animation/ViewUIComponent.java", + ":PlatformAnimationLib-aidl", + ], +} + +filegroup { name: "PlatformAnimationLib-aidl", srcs: [ "src/**/*.aidl", diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS new file mode 100644 index 000000000000..7569419bf4a4 --- /dev/null +++ b/packages/SystemUI/animation/lib/OWNERS @@ -0,0 +1,3 @@ +#inherits OWNERS from SystemUI in addition to WEAR framework owners below +file:platform/frameworks/base:/WEAR_OWNERS + diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java index 08db95e5a795..2b5ff7c4b598 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java @@ -44,6 +44,7 @@ import java.util.List; /** * An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin * and automatically attaches it to the transition leash before the transition starts. + * @hide */ public class OriginRemoteTransition extends IRemoteTransition.Stub { private static final String TAG = "OriginRemoteTransition"; @@ -328,7 +329,9 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub { /* baseBounds= */ maxBounds); } - /** An interface that represents an origin transitions. */ + /** An interface that represents an origin transitions. + * @hide + */ public interface TransitionPlayer { /** diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java index 23693b68a920..6d6aa8895ed0 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java @@ -43,6 +43,7 @@ import java.util.function.Supplier; /** * A session object that holds origin transition states for starting an activity from an on-screen * UI component and smoothly transitioning back from the activity to the same UI component. + * @hide */ public class OriginTransitionSession { private static final String TAG = "OriginTransitionSession"; @@ -179,7 +180,10 @@ public class OriginTransitionSession { } } - /** A builder to build a {@link OriginTransitionSession}. */ + /** + * A builder to build a {@link OriginTransitionSession}. + * @hide + */ public static class Builder { private final Context mContext; @Nullable private final IOriginTransitions mOriginTransitions; diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java index 24387360936b..e1ff2a4c8208 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java @@ -26,7 +26,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.concurrent.Executor; -/** A {@link UIComponent} representing a {@link SurfaceControl}. */ +/** + * A {@link UIComponent} representing a {@link SurfaceControl}. + * @hide + */ public class SurfaceUIComponent implements UIComponent { private final Collection<SurfaceControl> mSurfaces; private final Rect mBaseBounds; @@ -89,7 +92,10 @@ public class SurfaceUIComponent implements UIComponent { + "}"; } - /** A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. */ + /** + * A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. + * @hide + */ public static class Transaction implements UIComponent.Transaction<SurfaceUIComponent> { private final SurfaceControl.Transaction mTransaction; private final ArrayList<Runnable> mChanges = new ArrayList<>(); diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java index 5240d99a9217..64d21724ba32 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java @@ -27,6 +27,7 @@ import java.util.concurrent.Executor; /** * A composite {@link UIComponent.Transaction} that combines multiple other transactions for each ui * type. + * @hide */ public class Transactions implements UIComponent.Transaction<UIComponent> { private final Map<Class, UIComponent.Transaction> mTransactions = new ArrayMap<>(); diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java index 747e4d1eb278..8aec2f42a5f1 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java @@ -17,12 +17,17 @@ package com.android.systemui.animation; import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Rect; import android.view.SurfaceControl; import java.util.concurrent.Executor; -/** An interface representing an UI component on the display. */ +/** + * An interface representing an UI component on the display. + * @hide + */ public interface UIComponent { /** Get the current alpha of this UI. */ @@ -32,39 +37,48 @@ public interface UIComponent { boolean isVisible(); /** Get the bounds of this UI in its display. */ + @NonNull Rect getBounds(); /** Create a new {@link Transaction} that can update this UI. */ + @NonNull Transaction newTransaction(); /** * A transaction class for updating {@link UIComponent}. * * @param <T> the subtype of {@link UIComponent} that this {@link Transaction} can handle. + * @hide */ interface Transaction<T extends UIComponent> { /** Update alpha of an UI. Execution will be delayed until {@link #commit()} is called. */ - Transaction setAlpha(T ui, @FloatRange(from = 0.0, to = 1.0) float alpha); + Transaction setAlpha(@NonNull T ui, @FloatRange(from = 0.0, to = 1.0) float alpha); /** * Update visibility of an UI. Execution will be delayed until {@link #commit()} is called. */ - Transaction setVisible(T ui, boolean visible); + @NonNull + Transaction setVisible(@NonNull T ui, boolean visible); /** Update bounds of an UI. Execution will be delayed until {@link #commit()} is called. */ - Transaction setBounds(T ui, Rect bounds); + @NonNull + Transaction setBounds(@NonNull T ui, @NonNull Rect bounds); /** * Attach a ui to the transition leash. Execution will be delayed until {@link #commit()} is * called. */ - Transaction attachToTransitionLeash(T ui, SurfaceControl transitionLeash, int w, int h); + @NonNull + Transaction attachToTransitionLeash( + @NonNull T ui, @NonNull SurfaceControl transitionLeash, int w, int h); /** * Detach a ui from the transition leash. Execution will be delayed until {@link #commit} is * called. */ - Transaction detachFromTransitionLeash(T ui, Executor executor, Runnable onDone); + @NonNull + Transaction detachFromTransitionLeash( + @NonNull T ui, @NonNull Executor executor, @Nullable Runnable onDone); /** Commit any pending changes added to this transaction. */ void commit(); diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java index 313789c4ca7e..4c047d589a66 100644 --- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java +++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java @@ -38,6 +38,7 @@ import java.util.concurrent.Executor; * be changed to INVISIBLE in its view tree. This allows the {@link View} to transform in the * full-screen size leash without being constrained by the view tree's boundary or inheriting its * parent's alpha and transformation. + * @hide */ public class ViewUIComponent implements UIComponent { private static final String TAG = "ViewUIComponent"; @@ -234,6 +235,9 @@ public class ViewUIComponent implements UIComponent { mView.post(this::draw); } + /** + * @hide + */ public static class Transaction implements UIComponent.Transaction<ViewUIComponent> { private final List<Runnable> mChanges = new ArrayList<>(); diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt new file mode 100644 index 000000000000..47ad0b3e0cd4 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt @@ -0,0 +1,168 @@ +/* + * 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.animation.back + +import android.util.TimeUtils +import android.view.Choreographer +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_MOVE +import android.view.VelocityTracker +import android.view.animation.Interpolator +import android.window.BackEvent +import android.window.OnBackAnimationCallback +import com.android.app.animation.Interpolators +import com.android.internal.dynamicanimation.animation.DynamicAnimation +import com.android.internal.dynamicanimation.animation.FlingAnimation +import com.android.internal.dynamicanimation.animation.FloatValueHolder +import com.android.window.flags.Flags.predictiveBackTimestampApi + +private const val FLING_FRICTION = 6f +private const val SCALE_FACTOR = 100f + +/** + * Enhanced [OnBackAnimationCallback] with automatic fling animation and interpolated progress. + * + * Simplifies back gesture handling by animating flings and emitting processed events through + * `compat` functions. Customize progress interpolation with an optional [Interpolator]. + * + * @param progressInterpolator [Interpolator] for progress, defaults to + * [Interpolators.BACK_GESTURE]. + */ +abstract class FlingOnBackAnimationCallback( + val progressInterpolator: Interpolator = Interpolators.BACK_GESTURE +) : OnBackAnimationCallback { + + private val velocityTracker = VelocityTracker.obtain() + private var lastBackEvent: BackEvent? = null + private var downTime: Long? = null + + private var backInvokedFlingAnim: FlingAnimation? = null + private val backInvokedFlingUpdateListener = + DynamicAnimation.OnAnimationUpdateListener { _, progress: Float, _ -> + lastBackEvent?.let { + val backEvent = + BackEvent( + it.touchX, + it.touchY, + progress / SCALE_FACTOR, + it.swipeEdge, + it.frameTimeMillis, + ) + onBackProgressedCompat(backEvent) + } + } + private val backInvokedFlingEndListener = + DynamicAnimation.OnAnimationEndListener { _, _, _, _ -> + onBackInvokedCompat() + reset() + } + + abstract fun onBackStartedCompat(backEvent: BackEvent) + + abstract fun onBackProgressedCompat(backEvent: BackEvent) + + abstract fun onBackInvokedCompat() + + abstract fun onBackCancelledCompat() + + final override fun onBackStarted(backEvent: BackEvent) { + if (backInvokedFlingAnim != null) { + // This should never happen but let's call onBackInvokedCompat() just in case there is + // still a fling animation in progress + onBackInvokedCompat() + } + reset() + if (predictiveBackTimestampApi()) { + downTime = backEvent.frameTimeMillis + } + onBackStartedCompat(backEvent) + } + + final override fun onBackProgressed(backEvent: BackEvent) { + val interpolatedProgress = progressInterpolator.getInterpolation(backEvent.progress) + if (predictiveBackTimestampApi()) { + downTime?.let { downTime -> + velocityTracker.addMovement( + MotionEvent.obtain( + /* downTime */ downTime, + /* eventTime */ backEvent.frameTimeMillis, + /* action */ ACTION_MOVE, + /* x */ interpolatedProgress * SCALE_FACTOR, + /* y */ 0f, + /* metaState */ 0, + ) + ) + } + lastBackEvent = + BackEvent( + backEvent.touchX, + backEvent.touchY, + interpolatedProgress, + backEvent.swipeEdge, + backEvent.frameTimeMillis, + ) + } else { + lastBackEvent = + BackEvent( + backEvent.touchX, + backEvent.touchY, + interpolatedProgress, + backEvent.swipeEdge, + ) + } + lastBackEvent?.let { onBackProgressedCompat(it) } + } + + final override fun onBackInvoked() { + if (predictiveBackTimestampApi() && lastBackEvent != null) { + velocityTracker.computeCurrentVelocity(1000) + backInvokedFlingAnim = + FlingAnimation(FloatValueHolder()) + .setStartValue((lastBackEvent?.progress ?: 0f) * SCALE_FACTOR) + .setFriction(FLING_FRICTION) + .setStartVelocity(velocityTracker.xVelocity) + .setMinValue(0f) + .setMaxValue(SCALE_FACTOR) + .also { + it.addUpdateListener(backInvokedFlingUpdateListener) + it.addEndListener(backInvokedFlingEndListener) + it.start() + // do an animation-frame immediately to prevent idle frame + it.doAnimationFrame( + Choreographer.getInstance().lastFrameTimeNanos / TimeUtils.NANOS_PER_MS + ) + } + } else { + onBackInvokedCompat() + reset() + } + } + + final override fun onBackCancelled() { + onBackCancelledCompat() + reset() + } + + private fun reset() { + velocityTracker.clear() + backInvokedFlingAnim?.removeEndListener(backInvokedFlingEndListener) + backInvokedFlingAnim?.removeUpdateListener(backInvokedFlingUpdateListener) + lastBackEvent = null + backInvokedFlingAnim = null + downTime = null + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt index 8740d1467296..f708de3e9234 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt @@ -23,6 +23,7 @@ import android.window.BackEvent import android.window.OnBackAnimationCallback import android.window.OnBackInvokedDispatcher import android.window.OnBackInvokedDispatcher.Priority +import com.android.app.animation.Interpolators /** * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be @@ -40,16 +41,16 @@ fun onBackAnimationCallbackFrom( onBackInvoked: () -> Unit = {}, onBackCancelled: () -> Unit = {}, ): OnBackAnimationCallback { - return object : OnBackAnimationCallback { + return object : FlingOnBackAnimationCallback(progressInterpolator = Interpolators.LINEAR) { private var initialY = 0f private val lastTransformation = BackTransformation() - override fun onBackStarted(backEvent: BackEvent) { + override fun onBackStartedCompat(backEvent: BackEvent) { initialY = backEvent.touchY onBackStarted(backEvent) } - override fun onBackProgressed(backEvent: BackEvent) { + override fun onBackProgressedCompat(backEvent: BackEvent) { val progressY = (backEvent.touchY - initialY) / displayMetrics.heightPixels backAnimationSpec.getBackTransformation( @@ -61,11 +62,11 @@ fun onBackAnimationCallbackFrom( onBackProgressed(lastTransformation) } - override fun onBackInvoked() { + override fun onBackInvokedCompat() { onBackInvoked() } - override fun onBackCancelled() { + override fun onBackCancelledCompat() { onBackCancelled() } } @@ -86,7 +87,7 @@ fun View.registerOnBackInvokedCallbackOnViewAttached( override fun onViewAttachedToWindow(v: View) { onBackInvokedDispatcher.registerOnBackInvokedCallback( priority, - onBackAnimationCallback + onBackAnimationCallback, ) } diff --git a/packages/SystemUI/checks/Android.bp b/packages/SystemUI/checks/Android.bp index 04ac748d0c78..1ec22018c8c9 100644 --- a/packages/SystemUI/checks/Android.bp +++ b/packages/SystemUI/checks/Android.bp @@ -40,9 +40,11 @@ java_test_host { data: [ ":androidx.annotation_annotation", ":dagger2", - ":framework", ":kotlinx-coroutines-core", ], + device_common_data: [ + ":framework", + ], static_libs: [ "SystemUILintChecker", ], diff --git a/packages/SystemUI/common/Android.bp b/packages/SystemUI/common/Android.bp index 6fc13d7e0ddc..91dc3e338c59 100644 --- a/packages/SystemUI/common/Android.bp +++ b/packages/SystemUI/common/Android.bp @@ -22,20 +22,13 @@ package { default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], } -android_library { - +java_library { name: "SystemUICommon", - use_resource_processor: true, srcs: [ "src/**/*.java", "src/**/*.kt", ], - static_libs: [ - "androidx.core_core-ktx", - ], - - manifest: "AndroidManifest.xml", kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SystemUI/common/AndroidManifest.xml b/packages/SystemUI/common/AndroidManifest.xml deleted file mode 100644 index 6f757eb67d2e..000000000000 --- a/packages/SystemUI/common/AndroidManifest.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2023 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.common"> - -</manifest> diff --git a/packages/SystemUI/common/README.md b/packages/SystemUI/common/README.md index 1cc5277aa83e..17e541266965 100644 --- a/packages/SystemUI/common/README.md +++ b/packages/SystemUI/common/README.md @@ -1,5 +1,9 @@ # SystemUICommon -`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended to be used by other modules, and therefore should not have other SystemUI dependencies to avoid circular dependencies. +`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended +to be used by other modules, and therefore should not have other SystemUI dependencies to avoid +circular dependencies. -To maintain the structure of this module, please refrain from adding components at the top level. Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to keep the module organized and easy to navigate. +To maintain the structure of this module, please refrain from adding components at the top level. +Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to +keep the module organized and easy to navigate. diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt index c95d12032d0a..ae75e6c089ca 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt @@ -74,6 +74,8 @@ import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.android.systemui.animation.Expandable import com.android.systemui.animation.TransitionAnimator import kotlin.math.max @@ -173,9 +175,7 @@ fun Expandable( val wrappedContent = remember(content) { movableContentOf { expandable: Expandable -> - CompositionLocalProvider( - LocalContentColor provides contentColor, - ) { + CompositionLocalProvider(LocalContentColor provides contentColor) { // We make sure that the content itself (wrapped by the background) is at least // 40.dp, which is the same as the M3 buttons. This applies even if onClick is // null, to make it easier to write expandables that are sometimes clickable and @@ -253,7 +253,7 @@ fun Expandable( modifier .updateExpandableSize() .then(minInteractiveSizeModifier) - .drawWithContent { /* Don't draw anything when the dialog is shown. */} + .drawWithContent { /* Don't draw anything when the dialog is shown. */ } .onGloballyPositioned { controller.boundsInComposeViewRoot.value = it.boundsInRoot() } @@ -288,7 +288,7 @@ fun Expandable( .border(controller) .onGloballyPositioned { controller.boundsInComposeViewRoot.value = it.boundsInRoot() - }, + } ) { wrappedContent(controller.expandable) } @@ -365,19 +365,14 @@ private fun AnimatedContentInOverlay( } // Set the owners. - val overlayViewGroup = - getOverlayViewGroup( - context, - overlay, - ) + val overlayViewGroup = getOverlayViewGroup(context, overlay) overlayViewGroup.setViewTreeLifecycleOwner(composeViewRoot.findViewTreeLifecycleOwner()) overlayViewGroup.setViewTreeViewModelStoreOwner( composeViewRoot.findViewTreeViewModelStoreOwner() ) - ViewTreeSavedStateRegistryOwner.set( - overlayViewGroup, - ViewTreeSavedStateRegistryOwner.get(composeViewRoot), + overlayViewGroup.setViewTreeSavedStateRegistryOwner( + composeViewRoot.findViewTreeSavedStateRegistryOwner() ) composeView.setParentCompositionContext(compositionContext) @@ -405,10 +400,7 @@ private fun AnimatedContentInOverlay( } } -internal fun measureAndLayoutComposeViewInOverlay( - view: View, - state: TransitionAnimator.State, -) { +internal fun measureAndLayoutComposeViewInOverlay(view: View, state: TransitionAnimator.State) { val exactWidth = state.width val exactHeight = state.height view.measure( @@ -474,7 +466,7 @@ private fun ContentDrawScope.drawBackground( topLeft = Offset(halfStroke, halfStroke), size = Size(size.width - strokeWidth, size.height - strokeWidth), cornerRadius = cornerRadius.shrink(halfStroke), - style = borderStroke + style = borderStroke, ) } } else { @@ -494,11 +486,7 @@ private fun ContentDrawScope.drawBackground( if (border != null) { // Copied from androidx.compose.foundation.Border.kt. val strokeWidth = border.width.toPx() - val path = - createRoundRectPath( - (outline as Outline.Rounded).roundRect, - strokeWidth, - ) + val path = createRoundRectPath((outline as Outline.Rounded).roundRect, strokeWidth) drawPath(path, border.brush) } @@ -510,10 +498,7 @@ private fun ContentDrawScope.drawBackground( * * Copied from androidx.compose.foundation.Border.kt. */ -private fun createRoundRectPath( - roundedRect: RoundRect, - strokeWidth: Float, -): Path { +private fun createRoundRectPath(roundedRect: RoundRect, strokeWidth: Float): Path { return Path().apply { addRoundRect(roundedRect) val insetPath = @@ -532,7 +517,7 @@ private fun createInsetRoundedRect(widthPx: Float, roundedRect: RoundRect) = topLeftCornerRadius = roundedRect.topLeftCornerRadius.shrink(widthPx), topRightCornerRadius = roundedRect.topRightCornerRadius.shrink(widthPx), bottomLeftCornerRadius = roundedRect.bottomLeftCornerRadius.shrink(widthPx), - bottomRightCornerRadius = roundedRect.bottomRightCornerRadius.shrink(widthPx) + bottomRightCornerRadius = roundedRect.bottomRightCornerRadius.shrink(widthPx), ) /** diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt deleted file mode 100644 index cdc9a521b8c7..000000000000 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.compose.animation - -import android.view.View -import androidx.savedstate.SavedStateRegistryOwner -import androidx.savedstate.findViewTreeSavedStateRegistryOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner - -// TODO(b/262222023): Remove this workaround and import the new savedstate libraries in tm-qpr-dev -// instead. -object ViewTreeSavedStateRegistryOwner { - fun set(view: View, owner: SavedStateRegistryOwner?) { - view.setViewTreeSavedStateRegistryOwner(owner) - } - - fun get(view: View): SavedStateRegistryOwner? { - return view.findViewTreeSavedStateRegistryOwner() - } -} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/core/src/com/android/compose/grid/Grids.kt index f49939ba3a2d..f49939ba3a2d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/grid/Grids.kt diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/modifiers/ConditionalModifiers.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/ConditionalModifiers.kt index 135a6e4ec4e4..135a6e4ec4e4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/modifiers/ConditionalModifiers.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/ConditionalModifiers.kt diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt index 13c24642be4f..794b7a4a3d30 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt @@ -39,11 +39,7 @@ import androidx.compose.ui.unit.LayoutDirection * @param alpha alpha of the background * @param shape desired shape of the background */ -fun Modifier.background( - color: Color, - alpha: () -> Float, - shape: Shape = RectangleShape, -) = +fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) = this.then( FadingBackground( brush = SolidColor(color), @@ -56,7 +52,7 @@ fun Modifier.background( properties["color"] = color properties["alpha"] = alpha properties["shape"] = shape - } + }, ) ) @@ -65,7 +61,7 @@ constructor( private val brush: Brush, private val shape: Shape, private val alpha: () -> Float, - inspectorInfo: InspectorInfo.() -> Unit + inspectorInfo: InspectorInfo.() -> Unit, ) : DrawModifier, InspectorValueInfo(inspectorInfo) { // naive cache outline calculation if size is the same private var lastSize: Size? = null diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt new file mode 100644 index 000000000000..311519122312 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInContainer.kt @@ -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.compose.ui.graphics + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.drawscope.clipPath +import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.layer.GraphicsLayer +import androidx.compose.ui.graphics.layer.drawLayer +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.layout +import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.modifier.ModifierLocalModifierNode +import androidx.compose.ui.node.DrawModifierNode +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.requireDensity +import androidx.compose.ui.node.requireGraphicsContext +import androidx.compose.ui.node.requireLayoutCoordinates +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.util.fastForEach + +/** + * Define this as a container into which other composables can be drawn using [drawInContainer]. + * + * The elements redirected to this container will be drawn above the content of this composable. + */ +fun Modifier.container(state: ContainerState): Modifier { + return layout { measurable, constraints -> + val p = measurable.measure(constraints) + layout(p.width, p.height) { + val coords = coordinates + if (coords != null && !isLookingAhead) { + state.lastCoords = coords + } + + p.place(0, 0) + } + } + .drawWithContent { + drawContent() + state.drawInOverlay(this) + } +} + +/** + * Draw this composable into the container associated to [state]. + * + * @param state the state of the container into which we should draw this composable. + * @param enabled whether the redirection of the drawing to the container is enabled. + * @param zIndex the z-index of the composable in the container. + * @param clipPath the clip path applied when drawing this composable into the container. + */ +fun Modifier.drawInContainer( + state: ContainerState, + enabled: () -> Boolean = { true }, + zIndex: Float = 0f, + clipPath: (LayoutDirection, Density) -> Path? = { _, _ -> null }, +): Modifier { + return this.then( + DrawInContainerElement( + state = state, + enabled = enabled, + zIndex = zIndex, + clipPath = clipPath, + ) + ) +} + +class ContainerState { + private var renderers = mutableStateListOf<LayerRenderer>() + internal var lastCoords: LayoutCoordinates? = null + + internal fun onLayerRendererAttached(renderer: LayerRenderer) { + renderers.add(renderer) + renderers.sortBy { it.zIndex } + } + + internal fun onLayerRendererDetached(renderer: LayerRenderer) { + renderers.remove(renderer) + } + + internal fun drawInOverlay(drawScope: DrawScope) { + renderers.fastForEach { it.drawInOverlay(drawScope) } + } +} + +internal interface LayerRenderer { + val zIndex: Float + + fun drawInOverlay(drawScope: DrawScope) +} + +private data class DrawInContainerElement( + var state: ContainerState, + var enabled: () -> Boolean, + val zIndex: Float, + val clipPath: (LayoutDirection, Density) -> Path?, +) : ModifierNodeElement<DrawInContainerNode>() { + override fun create(): DrawInContainerNode { + return DrawInContainerNode(state, enabled, zIndex, clipPath) + } + + override fun update(node: DrawInContainerNode) { + node.state = state + node.enabled = enabled + node.zIndex = zIndex + node.clipPath = clipPath + } +} + +/** + * The implementation of [drawInContainer]. + * + * Note: this was forked from AndroidX RenderInTransitionOverlayNodeElement.kt + * (http://shortn/_3dfSFPbm8f). + */ +internal class DrawInContainerNode( + var state: ContainerState, + var enabled: () -> Boolean = { true }, + zIndex: Float = 0f, + var clipPath: (LayoutDirection, Density) -> Path? = { _, _ -> null }, +) : Modifier.Node(), DrawModifierNode, ModifierLocalModifierNode { + var zIndex by mutableFloatStateOf(zIndex) + + private inner class LayerWithRenderer(val layer: GraphicsLayer) : LayerRenderer { + override val zIndex: Float + get() = this@DrawInContainerNode.zIndex + + override fun drawInOverlay(drawScope: DrawScope) { + if (enabled()) { + with(drawScope) { + val containerCoords = + checkNotNull(state.lastCoords) { "container is not placed" } + val (x, y) = + requireLayoutCoordinates().positionInWindow() - + containerCoords.positionInWindow() + val clipPath = clipPath(layoutDirection, requireDensity()) + if (clipPath != null) { + clipPath(clipPath) { translate(x, y) { drawLayer(layer) } } + } else { + translate(x, y) { drawLayer(layer) } + } + } + } + } + } + + // Render in-place logic. Depending on the result of `renderInOverlay()`, the content will + // either render in-place or in the overlay, but never in both places. + override fun ContentDrawScope.draw() { + val layer = requireNotNull(layer) { "Error: layer never initialized" } + layer.record { this@draw.drawContent() } + if (!enabled()) { + drawLayer(layer) + } + } + + val layer: GraphicsLayer? + get() = layerWithRenderer?.layer + + private var layerWithRenderer: LayerWithRenderer? = null + + override fun onAttach() { + LayerWithRenderer(requireGraphicsContext().createGraphicsLayer()).let { + state.onLayerRendererAttached(it) + layerWithRenderer = it + } + } + + override fun onDetach() { + layerWithRenderer?.let { + state.onLayerRendererDetached(it) + requireGraphicsContext().releaseGraphicsLayer(it.layer) + } + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt new file mode 100644 index 000000000000..f5c3a834a8d7 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/graphics/DrawInOverlay.kt @@ -0,0 +1,79 @@ +/* + * 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.compose.ui.graphics + +import android.view.View +import android.view.ViewGroupOverlay +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCompositionContext +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.unit.IntSize +import androidx.lifecycle.findViewTreeLifecycleOwner +import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner + +/** + * Draw this composable in the [overlay][ViewGroupOverlay] of the [current ComposeView][LocalView]. + */ +@Composable +fun Modifier.drawInOverlay(): Modifier { + val containerState = remember { ContainerState() } + val context = LocalContext.current + val localView = LocalView.current + val compositionContext = rememberCompositionContext() + val displayMetrics = context.resources.displayMetrics + val displaySize = IntSize(displayMetrics.widthPixels, displayMetrics.heightPixels) + + DisposableEffect(containerState, context, localView, compositionContext, displaySize) { + val overlay = localView.rootView.overlay as ViewGroupOverlay + val view = + ComposeView(context).apply { + setParentCompositionContext(compositionContext) + + // Set the owners. + setViewTreeLifecycleOwner(localView.findViewTreeLifecycleOwner()) + setViewTreeViewModelStoreOwner(localView.findViewTreeViewModelStoreOwner()) + setViewTreeSavedStateRegistryOwner(localView.findViewTreeSavedStateRegistryOwner()) + + setContent { Box(Modifier.fillMaxSize().container(containerState)) } + } + + overlay.add(view) + + // Make the ComposeView as big as the display. We have to manually measure and layout the + // View given that there is no layout pass in Android overlays. + view.measure( + View.MeasureSpec.makeSafeMeasureSpec(displaySize.width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeSafeMeasureSpec(displaySize.height, View.MeasureSpec.EXACTLY), + ) + view.layout(0, 0, displaySize.width, displaySize.height) + + onDispose { overlay.remove(view) } + } + + return this.drawInContainer(containerState, enabled = { true }) +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 5feb63df0bca..1551ca97a2e3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -182,7 +182,8 @@ internal constructor( val itemBoundingBox = IntRect(item.offset, item.size) draggingItemKey != item.key && contentListState.isItemEditable(item.index) && - draggingBoundingBox.contains(itemBoundingBox.center) + (draggingBoundingBox.contains(itemBoundingBox.center) || + itemBoundingBox.contains(draggingBoundingBox.center)) } } else { state.layoutInfo.visibleItemsInfo diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt index c25a45dc5cf6..1475795e2dc6 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight @@ -40,7 +41,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.compose.modifiers.width import com.android.systemui.deviceentry.shared.model.BiometricMessage @@ -82,16 +82,13 @@ fun AlternateBouncer( Box( contentAlignment = Alignment.TopCenter, modifier = - Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f }) - .pointerInput(Unit) { - detectTapGestures( - onTap = { alternateBouncerDependencies.viewModel.onTapped() } - ) - }, + Modifier.background(color = Colors.AlternateBouncerBackgroundColor).pointerInput( + Unit + ) { + detectTapGestures(onTap = { alternateBouncerDependencies.viewModel.onTapped() }) + }, ) { - StatusMessage( - viewModel = alternateBouncerDependencies.messageAreaViewModel, - ) + StatusMessage(viewModel = alternateBouncerDependencies.messageAreaViewModel) } udfpsIconLocation?.let { udfpsLocation -> @@ -103,12 +100,7 @@ fun AlternateBouncer( Modifier.width { udfpsLocation.width } .height { udfpsLocation.height } .fillMaxHeight() - .offset { - IntOffset( - x = udfpsLocation.left, - y = udfpsLocation.top, - ) - }, + .offset { IntOffset(x = udfpsLocation.left, y = udfpsLocation.top) }, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt index 8b9e9274b448..5cb45e5bd914 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt @@ -18,7 +18,11 @@ package com.android.systemui.notifications.ui.composable import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.util.fastCoerceAtLeast +import androidx.compose.ui.util.fastCoerceAtMost import com.android.compose.nestedscroll.PriorityNestedScrollConnection +import com.android.compose.nestedscroll.ScrollController /** * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the @@ -44,7 +48,7 @@ fun NotificationScrimNestedScrollConnection( orientation = Orientation.Vertical, // scrolling up and inner content is taller than the scrim, so scrim needs to // expand; content can scroll once scrim is at the minScrimOffset. - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0 && offsetBeforeStart == 0f && contentHeight() > minVisibleScrimHeight() && @@ -52,36 +56,45 @@ fun NotificationScrimNestedScrollConnection( }, // scrolling down and content is done scrolling to top. After that, the scrim // needs to collapse; collapse the scrim until it is at the maxScrimOffset. - canStartPostScroll = { offsetAvailable, _ -> + canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) }, canStartPostFling = { false }, - canContinueScroll = { - val currentHeight = scrimOffset() - minScrimOffset() < currentHeight && currentHeight < maxScrimOffset - }, - canScrollOnFling = true, - onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable -> - val currentHeight = scrimOffset() - val amountConsumed = - if (offsetAvailable > 0) { - val amountLeft = maxScrimOffset - currentHeight - offsetAvailable.coerceAtMost(amountLeft) - } else { - val amountLeft = minScrimOffset() - currentHeight - offsetAvailable.coerceAtLeast(amountLeft) + onStart = { firstScroll -> + onStart(firstScroll) + object : ScrollController { + override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { + val currentHeight = scrimOffset() + val amountConsumed = + if (deltaScroll > 0) { + val amountLeft = maxScrimOffset - currentHeight + deltaScroll.fastCoerceAtMost(amountLeft) + } else { + val amountLeft = minScrimOffset() - currentHeight + deltaScroll.fastCoerceAtLeast(amountLeft) + } + snapScrimOffset(currentHeight + amountConsumed) + return amountConsumed } - snapScrimOffset(currentHeight + amountConsumed) - amountConsumed - }, - // Don't consume the velocity on pre/post fling - onStop = { velocityAvailable -> - onStop(velocityAvailable) - if (scrimOffset() < minScrimOffset()) { - animateScrimOffset(minScrimOffset()) + + override suspend fun onStop(initialVelocity: Float): Float { + onStop(initialVelocity) + if (scrimOffset() < minScrimOffset()) { + animateScrimOffset(minScrimOffset()) + } + // Don't consume the velocity on pre/post fling + return 0f + } + + override fun onCancel() { + onStop(0f) + if (scrimOffset() < minScrimOffset()) { + animateScrimOffset(minScrimOffset()) + } + } + + override fun canStopOnPreFling() = false } - { 0f } }, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt index a706585deebc..e1b74a968caa 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt @@ -23,12 +23,15 @@ import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastCoerceAtLeast import com.android.compose.nestedscroll.PriorityNestedScrollConnection +import com.android.compose.nestedscroll.ScrollController import kotlin.math.max import kotlin.math.roundToInt import kotlin.math.tanh @@ -86,21 +89,34 @@ fun NotificationStackNestedScrollConnection( ): PriorityNestedScrollConnection { return PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _ -> false }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { _, _, _ -> false }, + canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward() }, canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() }, - canContinueScroll = { stackOffset() > 0f }, - canScrollOnFling = true, - onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable -> - onScroll(offsetAvailable) - offsetAvailable - }, - onStop = { velocityAvailable -> - onStop(velocityAvailable) - suspend { velocityAvailable } + onStart = { firstScroll -> + onStart(firstScroll) + object : ScrollController { + override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { + val minOffset = 0f + val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset()) + if (consumed != 0f) { + onScroll(consumed) + } + return consumed + } + + override suspend fun onStop(initialVelocity: Float): Float { + onStop(initialVelocity) + return initialVelocity + } + + override fun onCancel() { + onStop(0f) + } + + override fun canStopOnPreFling() = false + } }, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt index e382e164d2d4..20c8c6a3f4bf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt @@ -72,7 +72,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.compose.animation.Expandable import com.android.compose.animation.scene.SceneScope -import com.android.compose.modifiers.background +import com.android.compose.modifiers.fadingBackground import com.android.compose.theme.colorAttr import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon @@ -107,7 +107,7 @@ fun SceneScope.FooterActionsWithAnimatedVisibility( animationSpec = tween(customizingAnimationDuration), targetHeight = { 0 }, ) + fadeOut(tween(customizingAnimationDuration)), - modifier = modifier.fillMaxWidth() + modifier = modifier.fillMaxWidth(), ) { QuickSettingsTheme { // This view has its own horizontal padding @@ -165,12 +165,8 @@ fun FooterActions( val contentColor = MaterialTheme.colorScheme.onSurface val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius) val backgroundModifier = - remember( - backgroundColor, - backgroundAlpha, - backgroundTopRadius, - ) { - Modifier.background( + remember(backgroundColor, backgroundAlpha, backgroundTopRadius) { + Modifier.fadingBackground( backgroundColor, backgroundAlpha::value, RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius), @@ -210,9 +206,7 @@ fun FooterActions( }, verticalAlignment = Alignment.CenterVertically, ) { - CompositionLocalProvider( - LocalContentColor provides contentColor, - ) { + CompositionLocalProvider(LocalContentColor provides contentColor) { if (security == null && foregroundServices == null) { Spacer(Modifier.weight(1f)) } @@ -238,19 +232,13 @@ private fun SecurityButton( { expandable -> onClick(context, expandable) } } - TextButton( - model.icon, - model.text, - showNewDot = false, - onClick = onClick, - modifier, - ) + TextButton(model.icon, model.text, showNewDot = false, onClick = onClick, modifier) } /** The foreground services button. */ @Composable private fun RowScope.ForegroundServicesButton( - model: FooterActionsForegroundServicesButtonViewModel, + model: FooterActionsForegroundServicesButtonViewModel ) { if (model.displayText) { TextButton( @@ -271,10 +259,7 @@ private fun RowScope.ForegroundServicesButton( /** A button with an icon. */ @Composable -private fun IconButton( - model: FooterActionsButtonViewModel, - modifier: Modifier = Modifier, -) { +private fun IconButton(model: FooterActionsButtonViewModel, modifier: Modifier = Modifier) { Expandable( color = colorAttr(model.backgroundColor), shape = CircleShape, @@ -282,11 +267,7 @@ private fun IconButton( modifier = modifier, ) { val tint = model.iconTint?.let { Color(it) } ?: Color.Unspecified - Icon( - model.icon, - tint = tint, - modifier = Modifier.size(20.dp), - ) + Icon(model.icon, tint = tint, modifier = Modifier.size(20.dp)) } } @@ -316,10 +297,7 @@ private fun NumberButton( Box( Modifier.fillMaxSize() .clip(CircleShape) - .indication( - interactionSource, - LocalIndication.current, - ) + .indication(interactionSource, LocalIndication.current) ) { Text( number.toString(), diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp index af1172bddfc8..682c49cfd19c 100644 --- a/packages/SystemUI/compose/scene/Android.bp +++ b/packages/SystemUI/compose/scene/Android.bp @@ -40,6 +40,8 @@ android_library { static_libs: [ "androidx.compose.runtime_runtime", "androidx.compose.material3_material3", + + "PlatformComposeCore", ], kotlincflags: ["-Xjvm-default=all"], 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 e5ef79b37b88..7c7202a5c7f2 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 @@ -20,6 +20,7 @@ package com.android.compose.animation.scene import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import androidx.compose.ui.util.fastCoerceIn @@ -27,9 +28,11 @@ import com.android.compose.animation.scene.content.Content import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.PriorityNestedScrollConnection -import com.android.compose.nestedscroll.SuspendedValue +import com.android.compose.nestedscroll.ScrollController import kotlin.math.absoluteValue +internal typealias SuspendedValue<T> = suspend () -> T + internal interface DraggableHandler { /** * Start a drag in the given [startedPosition], with the given [overSlop] and number of @@ -65,6 +68,7 @@ internal class DraggableHandlerImpl( internal val orientation: Orientation, ) : DraggableHandler { internal val nestedScrollKey = Any() + /** The [DraggableHandler] can only have one active [DragController] at a time. */ private var dragController: DragControllerImpl? = null @@ -344,6 +348,7 @@ private class DragControllerImpl( distance == DistanceUnspecified || swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) -> desiredOffset + distance > 0f -> desiredOffset.fastCoerceIn(0f, distance) else -> desiredOffset.fastCoerceIn(distance, 0f) } @@ -544,6 +549,7 @@ internal class Swipes( upOrLeftResult == null && downOrRightResult == null -> null (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null -> upOrLeftResult + else -> downOrRightResult } } @@ -607,12 +613,11 @@ internal class NestedScrollHandlerImpl( return overscrollSpec != null } - var dragController: DragController? = null var isIntercepting = false return PriorityNestedScrollConnection( orientation = orientation, - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> canChangeScene = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f @@ -644,7 +649,7 @@ internal class NestedScrollHandlerImpl( isIntercepting = true true }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> val behavior: NestedScrollBehavior = when { offsetAvailable > 0f -> topOrLeftBehavior @@ -668,10 +673,12 @@ internal class NestedScrollHandlerImpl( canChangeScene = isZeroOffset isZeroOffset && hasNextScene(offsetAvailable) } + NestedScrollBehavior.EdgeWithPreview -> { canChangeScene = isZeroOffset hasNextScene(offsetAvailable) } + NestedScrollBehavior.EdgeAlways -> { canChangeScene = true hasNextScene(offsetAvailable) @@ -709,38 +716,56 @@ internal class NestedScrollHandlerImpl( canStart }, - canContinueScroll = { true }, - canScrollOnFling = false, - onStart = { offsetAvailable -> + onStart = { firstScroll -> val pointersInfo = pointersInfo() - dragController = - draggableHandler.onDragStarted( - pointersDown = pointersInfo.pointersDown, - startedPosition = pointersInfo.startedPosition, - overSlop = if (isIntercepting) 0f else offsetAvailable, - ) + scrollController( + dragController = + draggableHandler.onDragStarted( + pointersDown = pointersInfo.pointersDown, + startedPosition = pointersInfo.startedPosition, + overSlop = if (isIntercepting) 0f else firstScroll, + ), + canChangeScene = canChangeScene, + pointersInfoOwner = pointersInfoOwner, + ) }, - onScroll = { offsetAvailable -> - val controller = dragController ?: error("Should be called after onStart") + ) + } +} - val pointersInfo = pointersInfoOwner.pointersInfo() - if (pointersInfo.isMouseWheel) { - // Do not support mouse wheel interactions - return@PriorityNestedScrollConnection 0f - } +private fun scrollController( + dragController: DragController, + canChangeScene: Boolean, + pointersInfoOwner: PointersInfoOwner, +): ScrollController { + return object : ScrollController { + override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return 0f + } - // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is - // initiated in a nested child. - controller.onDrag(delta = offsetAvailable) - }, - onStop = { velocityAvailable -> - val controller = dragController ?: error("Should be called after onStart") + return dragController.onDrag(delta = deltaScroll) + } - controller - .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) - .also { dragController = null } - }, - ) + override suspend fun onStop(initialVelocity: Float): Float { + return dragController + .onStop(velocity = initialVelocity, canChangeContent = canChangeScene) + .invoke() + } + + override fun onCancel() { + dragController.onStop(velocity = 0f, canChangeContent = canChangeScene) + } + + /** + * We need to maintain scroll priority even if the scene transition can no longer consume + * the scroll gesture to allow us to return to the previous scene. + */ + override fun canCancelScroll(available: Float, consumed: Float) = false + + override fun canStopOnPreFling() = true } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index ebe1df4bf55f..f14622fe7b65 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -48,10 +48,13 @@ import androidx.compose.ui.unit.round import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastForEachReversed import androidx.compose.ui.util.lerp +import com.android.compose.animation.scene.Element.State import com.android.compose.animation.scene.content.Content import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.PropertyTransformation import com.android.compose.animation.scene.transformation.SharedElementTransformation +import com.android.compose.modifiers.thenIf +import com.android.compose.ui.graphics.drawInContainer import com.android.compose.ui.util.lerp import kotlin.math.roundToInt import kotlinx.coroutines.launch @@ -146,10 +149,58 @@ internal fun Modifier.element( // TODO(b/341072461): Revert this and read the current transitions in ElementNode directly once // we can ensure that SceneTransitionLayoutImpl will compose new contents first. val currentTransitionStates = layoutImpl.state.transitionStates - return then(ElementModifier(layoutImpl, currentTransitionStates, content, key)) + return thenIf(layoutImpl.state.isElevationPossible(content.key, key)) { + Modifier.maybeElevateInContent(layoutImpl, content, key, currentTransitionStates) + } + .then(ElementModifier(layoutImpl, currentTransitionStates, content, key)) .testTag(key.testTag) } +private fun Modifier.maybeElevateInContent( + layoutImpl: SceneTransitionLayoutImpl, + content: Content, + key: ElementKey, + transitionStates: List<TransitionState>, +): Modifier { + fun isSharedElement( + stateByContent: Map<ContentKey, State>, + transition: TransitionState.Transition, + ): Boolean { + fun inFromContent() = transition.fromContent in stateByContent + fun inToContent() = transition.toContent in stateByContent + fun inCurrentScene() = transition.currentScene in stateByContent + + return if (transition is TransitionState.Transition.ReplaceOverlay) { + (inFromContent() && (inToContent() || inCurrentScene())) || + (inToContent() && inCurrentScene()) + } else { + inFromContent() && inToContent() + } + } + + return drawInContainer( + content.containerState, + enabled = { + val stateByContent = layoutImpl.elements.getValue(key).stateByContent + val state = elementState(transitionStates, isInContent = { it in stateByContent }) + + state is TransitionState.Transition && + state.transformationSpec + .transformations(key, content.key) + .shared + ?.elevateInContent == content.key && + isSharedElement(stateByContent, state) && + isSharedElementEnabled(key, state) && + shouldPlaceElement( + layoutImpl, + content.key, + layoutImpl.elements.getValue(key), + state, + ) + }, + ) +} + /** * An element associated to [ElementNode]. Note that this element does not support updates as its * arguments should always be the same. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index 8a0e4627d10c..fbd1cd542c05 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -128,10 +128,10 @@ private class NestedScrollToSceneNode( ) : DelegatingNode() { private var scrollBehaviorOwner: ScrollBehaviorOwner? = null - private fun requireScrollBehaviorOwner(): ScrollBehaviorOwner { + private fun findScrollBehaviorOwner(): ScrollBehaviorOwner? { var behaviorOwner = scrollBehaviorOwner if (behaviorOwner == null) { - behaviorOwner = requireScrollBehaviorOwner(layoutImpl.draggableHandler(orientation)) + behaviorOwner = findScrollBehaviorOwner(layoutImpl.draggableHandler(orientation)) scrollBehaviorOwner = behaviorOwner } return behaviorOwner @@ -156,8 +156,8 @@ private class NestedScrollToSceneNode( // transition between scenes. We can assume that the behavior is only needed if // there is some remaining amount. if (available != Offset.Zero) { - requireScrollBehaviorOwner() - .updateScrollBehaviors( + findScrollBehaviorOwner() + ?.updateScrollBehaviors( topOrLeftBehavior = topOrLeftBehavior, bottomOrRightBehavior = bottomOrRightBehavior, isExternalOverscrollGesture = isExternalOverscrollGesture, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index c9a4d5808cac..504240390674 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -127,22 +127,23 @@ interface SceneTransitionLayoutScope { /** A scope that can be used to query the target state of an element or scene. */ interface ElementStateScope { /** - * Return the *target* size of [this] element in the given [scene], i.e. the size of the element - * when idle, or `null` if the element is not composed and measured in that scene (yet). + * Return the *target* size of [this] element in the given [content], i.e. the size of the + * element when idle, or `null` if the element is not composed and measured in that content + * (yet). */ - fun ElementKey.targetSize(scene: SceneKey): IntSize? + fun ElementKey.targetSize(content: ContentKey): IntSize? /** - * Return the *target* offset of [this] element in the given [scene], i.e. the size of the - * element when idle, or `null` if the element is not composed and placed in that scene (yet). + * Return the *target* offset of [this] element in the given [content], i.e. the size of the + * element when idle, or `null` if the element is not composed and placed in that content (yet). */ - fun ElementKey.targetOffset(scene: SceneKey): Offset? + fun ElementKey.targetOffset(content: ContentKey): Offset? /** - * Return the *target* size of [this] scene, i.e. the size of the scene when idle, or `null` if - * the scene was never composed. + * Return the *target* size of [this] content, i.e. the size of the content when idle, or `null` + * if the content was not composed (yet). */ - fun SceneKey.targetSize(): IntSize? + fun ContentKey.targetSize(): IntSize? } @Stable diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 65c404387734..d58d3f24862f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -172,7 +172,12 @@ internal class SceneTransitionLayoutImpl( return scenes[key] ?: error("Scene $key is not configured") } - internal fun sceneOrNull(key: SceneKey): Scene? = scenes[key] + internal fun contentOrNull(key: ContentKey): Content? { + return when (key) { + is SceneKey -> scenes[key] + is OverlayKey -> overlays[key] + } + } internal fun overlay(key: OverlayKey): Overlay { return overlays[key] ?: error("Overlay $key is not configured") 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 a9a8668bd304..e1e2411da080 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 @@ -19,13 +19,16 @@ package com.android.compose.animation.scene import android.util.Log import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastAll +import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastForEach import com.android.compose.animation.scene.content.state.TransitionState +import com.android.compose.animation.scene.transformation.SharedElementTransformation import com.android.compose.animation.scene.transition.link.LinkedTransition import com.android.compose.animation.scene.transition.link.StateLink import kotlin.math.absoluteValue @@ -271,6 +274,14 @@ internal class MutableSceneTransitionLayoutStateImpl( mutableStateOf(listOf(TransitionState.Idle(initialScene, initialOverlays))) private set + /** + * The flattened list of [SharedElementTransformation] within all the transitions in + * [transitionStates]. + */ + private val transformationsWithElevation: List<SharedElementTransformation> by derivedStateOf { + transformationsWithElevation(transitionStates) + } + override val currentScene: SceneKey get() = transitionState.currentScene @@ -743,6 +754,42 @@ internal class MutableSceneTransitionLayoutStateImpl( animate() } + + private fun transformationsWithElevation( + transitionStates: List<TransitionState> + ): List<SharedElementTransformation> { + return buildList { + transitionStates.fastForEach { state -> + if (state !is TransitionState.Transition) { + return@fastForEach + } + + state.transformationSpec.transformations.fastForEach { transformation -> + if ( + transformation is SharedElementTransformation && + transformation.elevateInContent != null + ) { + add(transformation) + } + } + } + } + } + + /** + * Return whether we might need to elevate [element] (or any element if [element] is `null`) in + * [content]. + * + * This is used to compose `Modifier.container()` and `Modifier.drawInContainer()` only when + * necessary, for performance. + */ + internal fun isElevationPossible(content: ContentKey, element: ElementKey?): Boolean { + if (transformationsWithElevation.isEmpty()) return false + return transformationsWithElevation.fastAny { transformation -> + transformation.elevateInContent == content && + (element == null || transformation.matcher.matches(element, content)) + } + } } private const val TAG = "SceneTransitionLayoutState" diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index 205267da151a..f0043e1e89b0 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified -import com.android.compose.nestedscroll.SuspendedValue import kotlin.math.absoluteValue import kotlinx.coroutines.CompletableDeferred diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index a3f2a434cff7..fdf01cce396b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -172,15 +172,12 @@ private class SwipeToSceneNode( } /** Find the [ScrollBehaviorOwner] for the current orientation. */ -internal fun DelegatableNode.requireScrollBehaviorOwner( +internal fun DelegatableNode.findScrollBehaviorOwner( draggableHandler: DraggableHandlerImpl -): ScrollBehaviorOwner { - val ancestorNode = - checkNotNull(findNearestAncestor(draggableHandler.nestedScrollKey)) { - "This should never happen! Couldn't find a ScrollBehaviorOwner. " + - "Are we inside an SceneTransitionLayout?" - } - return ancestorNode as ScrollBehaviorOwner +): ScrollBehaviorOwner? { + // If there are no scenes in a particular orientation, the corresponding ScrollBehaviorOwnerNode + // is removed from the composition. + return findNearestAncestor(draggableHandler.nestedScrollKey) as? ScrollBehaviorOwner } internal fun interface ScrollBehaviorOwner { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt index e825c6e271ed..dc26b6b382b4 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt @@ -204,8 +204,17 @@ interface TransitionBuilder : BaseTransitionBuilder { * * @param enabled whether the matched element(s) should actually be shared in this transition. * Defaults to true. + * @param elevateInContent the content in which we should elevate the element when it is shared, + * drawing above all other composables of that content. If `null` (the default), we will + * simply draw this element in its original location. If not `null`, it has to be either the + * [fromContent][TransitionState.Transition.fromContent] or + * [toContent][TransitionState.Transition.toContent] of the transition. */ - fun sharedElement(matcher: ElementMatcher, enabled: Boolean = true) + fun sharedElement( + matcher: ElementMatcher, + enabled: Boolean = true, + elevateInContent: ContentKey? = null, + ) /** * Adds the transformations in [builder] but in reversed order. This allows you to partially diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt index a5ad999f7a64..269d91b02e7d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt @@ -249,8 +249,22 @@ internal class TransitionBuilderImpl(override val transition: TransitionState.Tr reversed = false } - override fun sharedElement(matcher: ElementMatcher, enabled: Boolean) { - transformations.add(SharedElementTransformation(matcher, enabled)) + override fun sharedElement( + matcher: ElementMatcher, + enabled: Boolean, + elevateInContent: ContentKey?, + ) { + check( + elevateInContent == null || + elevateInContent == transition.fromContent || + elevateInContent == transition.toContent + ) { + "elevateInContent (${elevateInContent?.debugName}) should be either fromContent " + + "(${transition.fromContent.debugName}) or toContent " + + "(${transition.toContent.debugName})" + } + + transformations.add(SharedElementTransformation(matcher, enabled, elevateInContent)) } override fun timestampRange( diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt index b7fa0d497200..690c809aa56d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt @@ -21,20 +21,20 @@ import androidx.compose.ui.unit.IntSize internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) : ElementStateScope { - override fun ElementKey.targetSize(scene: SceneKey): IntSize? { - return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetSize.takeIf { + override fun ElementKey.targetSize(content: ContentKey): IntSize? { + return layoutImpl.elements[this]?.stateByContent?.get(content)?.targetSize.takeIf { it != Element.SizeUnspecified } } - override fun ElementKey.targetOffset(scene: SceneKey): Offset? { - return layoutImpl.elements[this]?.stateByContent?.get(scene)?.targetOffset.takeIf { + override fun ElementKey.targetOffset(content: ContentKey): Offset? { + return layoutImpl.elements[this]?.stateByContent?.get(content)?.targetOffset.takeIf { it != Offset.Unspecified } } - override fun SceneKey.targetSize(): IntSize? { - return layoutImpl.sceneOrNull(this)?.targetSize.takeIf { it != IntSize.Zero } + override fun ContentKey.targetSize(): IntSize? { + return layoutImpl.contentOrNull(this)?.targetSize.takeIf { it != IntSize.Zero } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index c8407b13db66..8187e3932975 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -51,6 +51,9 @@ import com.android.compose.animation.scene.animateSharedValueAsState import com.android.compose.animation.scene.element import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions import com.android.compose.animation.scene.nestedScrollToScene +import com.android.compose.modifiers.thenIf +import com.android.compose.ui.graphics.ContainerState +import com.android.compose.ui.graphics.container /** A content defined in a [SceneTransitionLayout], i.e. a scene or an overlay. */ @Stable @@ -62,6 +65,7 @@ internal sealed class Content( zIndex: Float, ) { internal val scope = ContentScopeImpl(layoutImpl, content = this) + val containerState = ContainerState() var content by mutableStateOf(content) var zIndex by mutableFloatStateOf(zIndex) @@ -82,6 +86,9 @@ internal sealed class Content( val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.place(0, 0) } } + .thenIf(layoutImpl.state.isElevationPossible(content = key, element = null)) { + Modifier.container(containerState) + } .testTag(key.testTag) ) { scope.content() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt index 9bb302307359..de7f418f219a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -52,6 +52,7 @@ sealed interface Transformation { internal class SharedElementTransformation( override val matcher: ElementMatcher, internal val enabled: Boolean, + internal val elevateInContent: ContentKey?, ) : Transformation /** A transformation that changes the value of an element property, like its size or offset. */ diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt index 4ae323517b26..255da31719f3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt @@ -18,6 +18,9 @@ package com.android.compose.nestedscroll import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.util.fastCoerceAtLeast +import androidx.compose.ui.util.fastCoerceAtMost /** * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the @@ -43,35 +46,47 @@ fun LargeTopAppBarNestedScrollConnection( orientation = Orientation.Vertical, // When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will // expand. Then, you can then scroll down the content. - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight() }, // When swiping down, the content will scroll up until it reaches the top. Then, the // LargeTopAppBar will expand until it reaches its [maxHeight]. - canStartPostScroll = { offsetAvailable, _ -> + canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && height() < maxHeight() }, canStartPostFling = { false }, - canContinueScroll = { - val currentHeight = height() - minHeight() < currentHeight && currentHeight < maxHeight() - }, - canScrollOnFling = true, - onStart = { /* do nothing */ }, - onScroll = { offsetAvailable -> - val currentHeight = height() - val amountConsumed = - if (offsetAvailable > 0) { - val amountLeft = maxHeight() - currentHeight - offsetAvailable.coerceAtMost(amountLeft) - } else { - val amountLeft = minHeight() - currentHeight - offsetAvailable.coerceAtLeast(amountLeft) - } - onHeightChanged(currentHeight + amountConsumed) - amountConsumed - }, - // Don't consume the velocity on pre/post fling - onStop = { { 0f } }, + onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) }, ) } + +private class LargeTopAppBarScrollController( + val height: () -> Float, + val maxHeight: () -> Float, + val minHeight: () -> Float, + val onHeightChanged: (Float) -> Unit, +) : ScrollController { + override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { + val currentHeight = height() + val amountConsumed = + if (deltaScroll > 0) { + val amountLeft = maxHeight() - currentHeight + deltaScroll.fastCoerceAtMost(amountLeft) + } else { + val amountLeft = minHeight() - currentHeight + deltaScroll.fastCoerceAtLeast(amountLeft) + } + onHeightChanged(currentHeight + amountConsumed) + return amountConsumed + } + + override suspend fun onStop(initialVelocity: Float): Float { + // Don't consume the velocity on pre/post fling + return 0f + } + + override fun onCancel() { + // do nothing + } + + override fun canStopOnPreFling() = false +} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index a3641e6635e7..ca44a5c21cab 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -23,156 +23,316 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.Velocity import com.android.compose.ui.util.SpaceVectorConverter import kotlin.math.sign +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope -internal typealias SuspendedValue<T> = suspend () -> T +/** + * The [ScrollController] provides control over the scroll gesture. It allows you to: + * - Scroll the content by a given pixel amount. + * - Cancel the current scroll operation. + * - Stop the scrolling with a given initial velocity. + * + * **Important Notes:** + * - [onCancel] is called only when [PriorityNestedScrollConnection.reset] is invoked or when + * [canCancelScroll] returns `true` after a call to [onScroll]. It is never called after [onStop]. + * - [onStop] can be interrupted by a new gesture. In such cases, you need to handle a potential + * cancellation within your implementation of [onStop], although [onCancel] will not be called. + */ +interface ScrollController { + /** + * Scrolls the current content by [deltaScroll] pixels. + * + * @param deltaScroll The amount of pixels to scroll by. + * @param source The source of the scroll event. + * @return The amount of [deltaScroll] that was consumed. + */ + fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float + + /** + * Checks if the current scroll operation can be canceled. This is typically called after + * [onScroll] to determine if the [ScrollController] has lost priority and should cancel the + * ongoing scroll operation. + * + * @param available The total amount of scroll available. + * @param consumed The amount of scroll consumed by [onScroll]. + * @return `true` if the scroll can be canceled. + */ + fun canCancelScroll(available: Float, consumed: Float): Boolean { + return consumed == 0f + } + + /** + * Cancels the current scroll operation. This method is called when + * [PriorityNestedScrollConnection.reset] is invoked or when [canCancelScroll] returns `true`. + */ + fun onCancel() + + /** + * Checks if the scroll can be stopped during the [NestedScrollConnection.onPreFling] phase. + * + * @return `true` if the scroll can be stopped. + */ + fun canStopOnPreFling(): Boolean + + /** + * Stops the controller with the given [initialVelocity]. This typically starts a decay + * animation to smoothly bring the scrolling to a stop. This method can be interrupted by a new + * gesture, requiring you to handle potential cancellation within your implementation. + * + * @param initialVelocity The initial velocity of the scroll when stopping. + * @return The consumed [initialVelocity] when the animation completes. + */ + suspend fun onStop(initialVelocity: Float): Float +} /** - * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and - * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling. - * If it does, it will scroll before its children, until [canContinueScroll] allows it. + * A [NestedScrollConnection] that lets you implement custom scroll behaviors that take priority + * over the default nested scrolling logic. + * + * When started, this connection intercepts scroll events *before* they reach child composables. + * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll] + * or [canStartPostFling] returns `true`. + * + * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This + * controller allows you to directly manipulate the scroll state and define how scroll events are + * consumed. * - * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop] - * after [onStart]. + * **Important Considerations:** + * - When started, scroll events are typically consumed in `onPreScroll`. + * - The provided [ScrollController] should handle potential cancellation of `onStop` due to new + * gestures. + * - Use [reset] to release the current [ScrollController] and reset the connection to its initial + * state. * + * @param orientation The orientation of the scroll. + * @param canStartPreScroll A lambda that returns `true` if the connection should enter priority + * mode during the pre-scroll phase. This is called before child connections have a chance to + * consume the scroll. + * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority + * mode during the post-scroll phase. This is called after child connections have consumed the + * scroll. + * @param canStartPostFling A lambda that returns `true` if the connection should enter priority + * mode during the post-fling phase. This is called after a fling gesture has been initiated. + * @param onStart A lambda that is called when the connection enters priority mode. It should return + * a [ScrollController] that will be used to control the scroll. * @sample LargeTopAppBarNestedScrollConnection * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection */ class PriorityNestedScrollConnection( orientation: Orientation, - private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, - private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, + private val canStartPreScroll: + (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, + private val canStartPostScroll: + (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, private val canStartPostFling: (velocityAvailable: Float) -> Boolean, - private val canContinueScroll: (source: NestedScrollSource) -> Boolean, - private val canScrollOnFling: Boolean, - private val onStart: (offsetAvailable: Float) -> Unit, - private val onScroll: (offsetAvailable: Float) -> Float, - private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>, + private val onStart: (firstScroll: Float) -> ScrollController, ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) { - /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */ - private var isPriorityMode = false + /** The currently active [ScrollController], or `null` if not in priority mode. */ + private var currentController: ScrollController? = null - private var offsetScrolledBeforePriorityMode = 0f - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset { - val availableFloat = available.toFloat() - // The offset before the start takes into account the up and down movements, starting from - // the beginning or from the last fling gesture. - val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat + /** + * A [Deferred] representing the ongoing `onStop` animation. Used to interrupt the animation if + * a new gesture occurs. + */ + private var stoppingJob: Deferred<Float>? = null - if ( - isPriorityMode || - (source == NestedScrollSource.SideEffect && !canScrollOnFling) || - !canStartPostScroll(availableFloat, offsetBeforeStart) - ) { - // The priority mode cannot start so we won't consume the available offset. - return Offset.Zero - } + /** + * Indicates whether the connection is currently in the process of stopping the scroll with the + * [ScrollController.onStop] animation. + */ + private val isStopping + get() = stoppingJob?.isActive ?: false - return onPriorityStart(availableFloat).toOffset() - } + /** + * Tracks the cumulative scroll offset that has been consumed by other composables before this + * connection enters priority mode. This is used to determine when the connection should take + * over scrolling based on the [canStartPreScroll] and [canStartPostScroll] conditions. + */ + private var offsetScrolledBeforePriorityMode = 0f override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - if (!isPriorityMode) { - if (source == NestedScrollSource.UserInput || canScrollOnFling) { - val availableFloat = available.toFloat() - if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) { - return onPriorityStart(availableFloat).toOffset() - } - // We want to track the amount of offset consumed before entering priority mode - offsetScrolledBeforePriorityMode += availableFloat - } + // If stopping, interrupt the animation and clear the controller. + if (isStopping) { + interruptStopping() + } - return Offset.Zero + // If in priority mode, consume the scroll using the current controller. + if (currentController != null) { + return scroll(available.toFloat(), source) } + // Check if pre-scroll condition is met, and start priority mode if necessary. val availableFloat = available.toFloat() - if (!canContinueScroll(source)) { - // Step 3a: We have lost priority and we no longer need to intercept scroll events. - onPriorityStop(velocity = 0f) + if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) { + start(availableFloat) + return scroll(availableFloat, source) + } - // We've just reset offsetScrolledBeforePriorityMode to 0f - // We want to track the amount of offset consumed before entering priority mode - offsetScrolledBeforePriorityMode += availableFloat + // Track offset consumed before entering priority mode. + offsetScrolledBeforePriorityMode += availableFloat + return Offset.Zero + } - return Offset.Zero + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + // If in priority mode, scroll events are consumed only in pre-scroll phase. + if (currentController != null) return Offset.Zero + + // Check if post-scroll condition is met, and start priority mode if necessary. + val availableFloat = available.toFloat() + val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat + if (canStartPostScroll(availableFloat, offsetBeforeStart, source)) { + start(availableFloat) + return scroll(availableFloat, source) } - // Step 2: We have the priority and can consume the scroll events. - return onScroll(availableFloat).toOffset() + // Do not consume the offset if priority mode is not activated. + return Offset.Zero } override suspend fun onPreFling(available: Velocity): Velocity { - if (isPriorityMode && canScrollOnFling) { - // We don't want to consume the velocity, we prefer to continue receiving scroll events. - return Velocity.Zero + val controller = currentController ?: return Velocity.Zero + + // If in priority mode and can stop on pre-fling phase, stop the scroll. + if (controller.canStopOnPreFling()) { + return stop(velocity = available.toFloat()) } - // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed - // of the fling gesture. - return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity() + + // Do not consume the velocity if not stopping on pre-fling phase. + return Velocity.Zero } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { val availableFloat = available.toFloat() - if (isPriorityMode) { - return onPriorityStop(velocity = availableFloat).invoke().toVelocity() - } + val controller = currentController - if (!canStartPostFling(availableFloat)) { - return Velocity.Zero + // If in priority mode, stop the scroll. + if (controller != null) { + return stop(velocity = availableFloat) } - // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of 1px - // given the available velocity. + // Check if post-fling condition is met, and start priority mode if necessary. // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the // overscroll behavior on the Scene level. - val smallOffset = availableFloat.sign - onPriorityStart(availableOffset = smallOffset) + if (canStartPostFling(availableFloat)) { + // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of + // 1px given the available velocity. + val smallOffset = availableFloat.sign + start(availableOffset = smallOffset) + return stop(availableFloat) + } - // This is the last event of a scroll gesture. - return onPriorityStop(availableFloat).invoke().toVelocity() + // Reset offset tracking after the fling gesture is finished. + resetOffsetTracker() + return Velocity.Zero } /** - * Method to call before destroying the object or to reset the initial state. - * - * TODO(b/303224944) This method should be removed. + * Resets the connection to its initial state. This cancels any ongoing scroll operation and + * clears the current [ScrollController]. */ fun reset() { - // Step 3c: To ensure that an onStop is always called for every onStart. - onPriorityStop(velocity = 0f) + if (currentController != null && !isStopping) { + cancel() + } else { + resetOffsetTracker() + } } - private fun onPriorityStart(availableOffset: Float): Float { - if (isPriorityMode) { - error("This should never happen, onPriorityStart() was called when isPriorityMode") + /** + * Starts priority mode by creating a new [ScrollController] using the [onStart] lambda. + * + * @param availableOffset The initial scroll offset available. + */ + private fun start(availableOffset: Float) { + check(currentController == null) { "Another controller is active: $currentController" } + + resetOffsetTracker() + + currentController = onStart(availableOffset) + } + + /** + * Retrieves the current [ScrollController], ensuring that it is not null and that the + * [isStopping] state matches the expected value. + */ + private fun requireController(isStopping: Boolean): ScrollController { + check(this.isStopping == isStopping) { + "isStopping is ${this.isStopping}, instead of $isStopping" + } + check(offsetScrolledBeforePriorityMode == 0f) { + "offset scrolled should be zero, but it was $offsetScrolledBeforePriorityMode" } + return checkNotNull(currentController) { "The controller is $currentController" } + } - // Step 1: It's our turn! We start capturing scroll events when one of our children has an - // available offset following a scroll event. - isPriorityMode = true + /** + * Scrolls the content using the current [ScrollController]. + * + * @param delta The amount of scroll to apply. + * @param source The source of the scroll event. + * @return The amount of scroll consumed. + */ + private fun scroll(delta: Float, source: NestedScrollSource): Offset { + val controller = requireController(isStopping = false) + val consumedByScroll = controller.onScroll(delta, source) - // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is - // lifted (step 3b), or this object has been destroyed (step 3c). - onStart(availableOffset) + if (controller.canCancelScroll(delta, consumedByScroll)) { + // We have lost priority and we no longer need to intercept scroll events. + cancel() + offsetScrolledBeforePriorityMode = delta - consumedByScroll + } - return onScroll(availableOffset) + return consumedByScroll.toOffset() } - private fun onPriorityStop(velocity: Float): SuspendedValue<Float> { - // We can restart tracking the consumed offsets from scratch. - offsetScrolledBeforePriorityMode = 0f + /** Cancels the current scroll operation and clears the current [ScrollController]. */ + private fun cancel() { + requireController(isStopping = false).onCancel() + currentController = null + } - if (!isPriorityMode) { - return { 0f } + /** + * Stops the scroll with the given velocity using the current [ScrollController]. + * + * @param velocity The velocity to stop with. + * @return The consumed velocity. + */ + suspend fun stop(velocity: Float): Velocity { + val controller = requireController(isStopping = false) + return coroutineScope { + try { + async { controller.onStop(velocity) } + // Allows others to interrupt the job. + .also { stoppingJob = it } + // Note: this can be cancelled by [interruptStopping] + .await() + .toVelocity() + } finally { + // If the job is interrupted, it might take a while to cancel. We need to make sure + // the current controller is still the initial one. + if (currentController == controller) { + currentController = null + } + } } + } - isPriorityMode = false + /** Interrupts the ongoing stop animation and clears the current [ScrollController]. */ + private fun interruptStopping() { + requireController(isStopping = true) + // We are throwing a CancellationException in the [ScrollController.onStop] method. + stoppingJob?.cancel() + currentController = null + } - return onStop(velocity) + /** Resets the tracking of consumed offsets before entering priority mode. */ + private fun resetOffsetTracker() { + offsetScrolledBeforePriorityMode = 0f } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index fd0214823eaf..f24d93f0d79d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -39,7 +39,6 @@ import com.android.compose.animation.scene.TestScenes.SceneC import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.Transition import com.android.compose.animation.scene.subjects.assertThat -import com.android.compose.nestedscroll.SuspendedValue import com.android.compose.test.MonotonicClockTestScope import com.android.compose.test.runMonotonicClockTest import com.android.compose.test.transition @@ -850,6 +849,34 @@ class DraggableHandlerTest { } @Test + fun duringATransition_aNewScrollGesture_shouldTakeControl() = runGestureTest { + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) + // First gesture + nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) + assertTransition(currentScene = SceneA) + nestedScroll.preFling(available = Velocity.Zero) + assertTransition(currentScene = SceneA) + + // Second gesture, it starts during onStop() animation + nestedScroll.scroll(downOffset(0.1f)) + assertTransition(currentScene = SceneA) + + // Allows onStop() to complete or cancel + advanceUntilIdle() + + // Second gesture continues + nestedScroll.scroll(downOffset(0.1f)) + assertTransition(currentScene = SceneA) + + // Second gesture ends + nestedScroll.preFling(available = Velocity.Zero) + assertTransition(currentScene = SceneA) + + advanceUntilIdle() + assertIdle(currentScene = SceneA) + } + + @Test fun onPreFling_velocityLowerThanThreshold_remainSameScene() = runGestureTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.1f)) @@ -1296,6 +1323,26 @@ class DraggableHandlerTest { } @Test + fun scrollKeepPriorityEvenIfWeCanNoLongerScrollOnThatDirection() = runGestureTest { + // Overscrolling on scene B does nothing. + layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) } + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Overscroll is disabled, it will scroll up to 100% + nestedScroll.scroll(available = upOffset(fractionOfScreen = 2f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f) + + // We need to maintain scroll priority even if the scene transition can no longer consume + // the scroll gesture. + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 1f) + + // A scroll gesture in the opposite direction allows us to return to the previous scene. + nestedScroll.scroll(available = downOffset(fractionOfScreen = 0.5f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.5f) + } + + @Test fun overscroll_releaseBetween0And100Percent_up() = runGestureTest { // Make scene B overscrollable. layoutState.transitions = transitions { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt index c8f6e6d99933..3df608717414 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Velocity import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.modifiers.thenIf -import com.android.compose.nestedscroll.SuspendedValue import com.google.common.truth.Truth.assertThat import kotlin.properties.Delegates import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt index 5edb99ea0795..37dae39f935d 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt @@ -225,6 +225,40 @@ class NestedScrollToSceneTest { } @Test + fun stlNotConsumeUnobservedGesture() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutState(SceneA, transitions = EmptyTestTransitions) + } + + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout( + state = state, + modifier = Modifier.size(layoutWidth, layoutHeight), + ) { + scene(SceneA) { + Spacer( + Modifier.verticalNestedScrollToScene() + // This scrollable will not consume the gesture. + .scrollable(rememberScrollableState { 0f }, Vertical) + .fillMaxSize() + ) + } + } + } + + rule.onRoot().performTouchInput { + down(Offset.Zero) + // There is no vertical scene. + moveBy(Offset(0f, layoutWidth.toPx()), delayMillis = 1_000) + } + + rule.waitForIdle() + assertThat(state.transitionState).isIdle() + } + + @Test fun customizeStlNestedScrollBehavior_multipleRequests() { var canScroll = true val state = setup2ScenesAndScrollTouchSlop { diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 3001505d0e02..2bc9b3826548 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -938,6 +938,71 @@ class SwipeToSceneTest { } @Test + fun scrollKeepPriorityEvenIfWeCanNoLongerScrollOnThatDirection() { + val swipeDistance = 100.dp + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutState( + SceneA, + transitions { + from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) } + from(SceneB, to = SceneC) { distance = FixedDistance(swipeDistance) } + overscrollDisabled(SceneB, Orientation.Vertical) + }, + ) + } + val layoutSize = 200.dp + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(state, Modifier.size(layoutSize)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB, Swipe.Right to SceneC)) { + Box( + Modifier.fillMaxSize() + // A scrollable that does not consume the scroll gesture + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + scene(SceneB, userActions = mapOf(Swipe.Right to SceneC)) { + Box(Modifier.element(TestElements.Foo).fillMaxSize()) + } + scene(SceneC) { Box(Modifier.fillMaxSize()) } + } + } + + fun assertTransition(from: SceneKey, to: SceneKey, progress: Float) { + val transition = assertThat(state.transitionState).isSceneTransition() + assertThat(transition).hasFromScene(from) + assertThat(transition).hasToScene(to) + assertThat(transition.progress).isEqualTo(progress) + } + + // Vertical scroll 100% + rule.onRoot().performTouchInput { + val middle = (layoutSize / 2).toPx() + down(Offset(middle, middle)) + moveBy(Offset(0f, y = touchSlop + swipeDistance.toPx()), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Continue vertical scroll, should be ignored (overscrollDisabled) + rule.onRoot().performTouchInput { moveBy(Offset(0f, y = touchSlop), delayMillis = 1_000) } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Horizontal scroll, should be ignored + rule.onRoot().performTouchInput { + moveBy(Offset(x = touchSlop + swipeDistance.toPx(), 0f), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 1f) + + // Vertical scroll, in the opposite direction + rule.onRoot().performTouchInput { + moveBy(Offset(0f, -swipeDistance.toPx()), delayMillis = 1_000) + } + assertTransition(from = SceneA, to = SceneB, progress = 0f) + } + + @Test fun sceneWithoutSwipesDoesNotConsumeGestures() { val buttonTag = "button" diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt index badc43bd3e0f..0364cdc4166e 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt @@ -20,6 +20,7 @@ package com.android.compose.nestedscroll import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput import androidx.compose.ui.unit.Velocity import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -34,29 +35,39 @@ class PriorityNestedScrollConnectionTest { private var canStartPreScroll = false private var canStartPostScroll = false private var canStartPostFling = false - private var canContinueScroll = false + private var canStopOnPreFling = true private var isStarted = false private var lastScroll: Float? = null - private var returnOnScroll = 0f + private var consumeScroll = true private var lastStop: Float? = null - private var returnOnStop = 0f + private var isCancelled: Boolean = false + private var consumeStop = true private val scrollConnection = PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _ -> canStartPreScroll }, - canStartPostScroll = { _, _ -> canStartPostScroll }, + canStartPreScroll = { _, _, _ -> canStartPreScroll }, + canStartPostScroll = { _, _, _ -> canStartPostScroll }, canStartPostFling = { canStartPostFling }, - canContinueScroll = { canContinueScroll }, - canScrollOnFling = false, - onStart = { isStarted = true }, - onScroll = { - lastScroll = it - returnOnScroll - }, - onStop = { - lastStop = it - { returnOnStop } + onStart = { _ -> + isStarted = true + object : ScrollController { + override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { + lastScroll = deltaScroll + return if (consumeScroll) deltaScroll else 0f + } + + override suspend fun onStop(initialVelocity: Float): Float { + lastStop = initialVelocity + return if (consumeStop) initialVelocity else 0f + } + + override fun onCancel() { + isCancelled = true + } + + override fun canStopOnPreFling() = canStopOnPreFling + } }, ) @@ -85,7 +96,7 @@ class PriorityNestedScrollConnectionTest { canStartPostScroll = true scrollConnection.onPostScroll( consumed = Offset.Zero, - available = Offset.Zero, + available = Offset(1f, 1f), source = UserInput, ) } @@ -136,45 +147,55 @@ class PriorityNestedScrollConnectionTest { @Test fun step2_onPriorityMode_shouldContinueIfAllowed() { startPriorityModePostScroll() - canContinueScroll = true - scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput) + val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) assertThat(lastScroll).isEqualTo(1f) + assertThat(scroll1.y).isEqualTo(1f) - canContinueScroll = false - scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput) - assertThat(lastScroll).isNotEqualTo(2f) - assertThat(lastScroll).isEqualTo(1f) + consumeScroll = false + val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput) + assertThat(lastScroll).isEqualTo(2f) + assertThat(scroll2.y).isEqualTo(0f) } @Test - fun step3a_onPriorityMode_shouldStopIfCannotContinue() { + fun step3a_onPriorityMode_shouldCancelIfCannotContinue() { startPriorityModePostScroll() - canContinueScroll = false + consumeScroll = false - scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput) + scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) - assertThat(lastStop).isNotNull() + assertThat(isCancelled).isTrue() } @Test fun step3b_onPriorityMode_shouldStopOnFling() = runTest { startPriorityModePostScroll() - canContinueScroll = true scrollConnection.onPreFling(available = Velocity.Zero) - assertThat(lastStop).isNotNull() + assertThat(lastStop).isEqualTo(0f) + } + + @Test + fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest { + startPriorityModePostScroll() + canStopOnPreFling = false + + scrollConnection.onPreFling(available = Velocity.Zero) + assertThat(lastStop).isNull() + + scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) + assertThat(lastStop).isEqualTo(0f) } @Test - fun step3c_onPriorityMode_shouldStopOnReset() { + fun step3c_onPriorityMode_shouldCancelOnReset() { startPriorityModePostScroll() - canContinueScroll = true scrollConnection.reset() - assertThat(lastStop).isNotNull() + assertThat(isCancelled).isTrue() } @Test diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt index 031fbabb239f..2a2d33308d12 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt @@ -17,34 +17,24 @@ package com.android.systemui.shared.clocks import android.content.Context -import android.content.res.ColorStateList import android.content.res.Resources -import android.graphics.Color import android.graphics.Typeface import android.graphics.drawable.Drawable import android.util.TypedValue -import com.android.internal.graphics.ColorUtils -import com.android.internal.graphics.cam.Cam -import com.android.internal.graphics.cam.CamUtils import com.android.internal.policy.SystemBarUtils import com.android.systemui.log.core.Logger import com.android.systemui.log.core.MessageBuffer -import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style as MonetStyle -import com.android.systemui.monet.TonalPalette import java.io.IOException -import kotlin.math.abs class AssetLoader private constructor( private val pluginCtx: Context, private val sysuiCtx: Context, private val baseDir: String, - var colorScheme: ColorScheme?, var seedColor: Int?, var overrideChroma: Float?, val typefaceCache: TypefaceCache, - val getThemeSeedColor: (Context) -> Int, messageBuffer: MessageBuffer, ) { val logger = Logger(messageBuffer, TAG) @@ -59,12 +49,10 @@ private constructor( sysuiCtx: Context, baseDir: String, messageBuffer: MessageBuffer, - getThemeSeedColor: ((Context) -> Int)? = null, ) : this( pluginCtx, sysuiCtx, baseDir, - colorScheme = null, seedColor = null, overrideChroma = null, typefaceCache = @@ -72,7 +60,6 @@ private constructor( // TODO(b/364680873): Move constant to config_clockFontFamily when shipping return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) }, - getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor, messageBuffer = messageBuffer, ) @@ -92,107 +79,6 @@ private constructor( return res.getString(id) } - fun tryReadColor(resStr: String): Int? = tryRead(resStr, ::readColor) - - fun readColor(resStr: String): Int { - if (resStr.startsWith("#")) { - return Color.parseColor(resStr) - } - - val schemeColor = tryParseColorFromScheme(resStr) - if (schemeColor != null) { - logColor("ColorScheme: $resStr", schemeColor) - return checkChroma(schemeColor) - } - - val result = resolveColorResourceId(resStr) - if (result == null) { - throw IOException("Failed to parse color: $resStr") - } - - val (res, colorId, targetTone) = result - val color = res.getColor(colorId) - if (targetTone == null || TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) { - logColor("Resources: $resStr", color) - return checkChroma(color) - } else { - val interpolatedColor = - ColorStateList.valueOf(color) - .withLStar((1000f - targetTone) / 10f) - .getDefaultColor() - logColor("Resources (interpolated tone): $resStr", interpolatedColor) - return checkChroma(interpolatedColor) - } - } - - private fun checkChroma(color: Int): Int { - return overrideChroma?.let { - val cam = Cam.fromInt(color) - val tone = CamUtils.lstarFromInt(color) - val result = ColorUtils.CAMToColor(cam.hue, it, tone) - logColor("Chroma override", result) - result - } ?: color - } - - private fun tryParseColorFromScheme(resStr: String): Int? { - val colorScheme = this.colorScheme - if (colorScheme == null) { - logger.w("No color scheme available") - return null - } - - val (packageName, category, name) = parseResourceId(resStr) - if (packageName != "android" || category != "color") { - logger.w("Failed to parse package from $resStr") - return null - } - - var parts = name.split('_') - if (parts.size != 3) { - logger.w("Failed to find palette and shade from $name") - return null - } - val (_, paletteKey, shadeKeyStr) = parts - - val palette = - when (paletteKey) { - "accent1" -> colorScheme.accent1 - "accent2" -> colorScheme.accent2 - "accent3" -> colorScheme.accent3 - "neutral1" -> colorScheme.neutral1 - "neutral2" -> colorScheme.neutral2 - else -> return null - } - - if (shadeKeyStr.contains("+") || shadeKeyStr.contains("-")) { - val signIndex = shadeKeyStr.indexOfLast { it == '-' || it == '+' } - // Use the tone of the seed color if it was set explicitly. - var baseTone = - if (seedColor != null) colorScheme.seedTone.toFloat() - else shadeKeyStr.substring(0, signIndex).toFloatOrNull() - val diff = shadeKeyStr.substring(signIndex).toFloatOrNull() - - if (baseTone == null) { - logger.w("Failed to parse base tone from $shadeKeyStr") - return null - } - - if (diff == null) { - logger.w("Failed to parse relative tone from $shadeKeyStr") - return null - } - return palette.getAtTone(baseTone + diff) - } else { - val shadeKey = shadeKeyStr.toIntOrNull() - if (shadeKey == null) { - logger.w("Failed to parse tone from $shadeKeyStr") - return null - } - return palette.allShadesMapped.get(shadeKey) ?: palette.getAtTone(shadeKey.toFloat()) - } - } - fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr) fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset) @@ -250,52 +136,6 @@ private constructor( } } - fun resolveColorResourceId(resStr: String): Triple<Resources, Int, Float?>? { - var (packageName, category, name) = parseResourceId(resStr) - - // Convert relative tonal specifiers to standard - val relIndex = name.indexOfLast { it == '_' } - val isToneRelative = name.contains("-") || name.contains("+") - val targetTone = - if (packageName != "android") { - null - } else if (isToneRelative) { - val signIndex = name.indexOfLast { it == '-' || it == '+' } - val baseTone = name.substring(relIndex + 1, signIndex).toFloatOrNull() - var diff = name.substring(signIndex).toFloatOrNull() - if (baseTone == null || diff == null) { - logger.w("Failed to parse relative tone from $name") - return null - } - baseTone + diff - } else { - val absTone = name.substring(relIndex + 1).toFloatOrNull() - if (absTone == null) { - logger.w("Failed to parse absolute tone from $name") - return null - } - absTone - } - - if ( - targetTone != null && - (isToneRelative || !TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) - ) { - val closeTone = TonalPalette.SHADE_KEYS.minBy { abs(it - targetTone) } - val prevName = name - name = name.substring(0, relIndex + 1) + closeTone - logger.i("Converted $prevName to $name") - } - - val result = resolveResourceId(packageName, category, name) - if (result == null) { - return null - } - - val (res, resId) = result - return Triple(res, resId, targetTone) - } - fun resolveResourceId(resStr: String): Pair<Resources, Int>? { val (packageName, category, name) = parseResourceId(resStr) return resolveResourceId(packageName, category, name) @@ -331,8 +171,7 @@ private constructor( try { if (path.startsWith("@")) { val pair = resolveResourceId(path) - val colorPair = resolveColorResourceId(path) - return pair != null || colorPair != null + return pair != null } else { val stream = pluginCtx.resources.assets.open("$baseDir$path") if (stream == null) { @@ -352,37 +191,14 @@ private constructor( pluginCtx, sysuiCtx, baseDir, - colorScheme, seedColor, overrideChroma, typefaceCache, - getThemeSeedColor, messageBuffer ?: logger.buffer, ) fun setSeedColor(seedColor: Int?, style: MonetStyle?) { this.seedColor = seedColor - refreshColorPalette(style) - } - - fun refreshColorPalette(style: MonetStyle?) { - val seedColor = - this.seedColor ?: getThemeSeedColor(sysuiCtx).also { logColor("Theme Seed Color", it) } - this.colorScheme = - ColorScheme( - seedColor, - false, // darkTheme is not used for palette generation - style ?: MonetStyle.CLOCK, - ) - - // Enforce low chroma on output colors if low chroma theme is selected - this.overrideChroma = run { - val cam = colorScheme?.seed?.let { Cam.fromInt(it) } - if (cam != null && cam.chroma < LOW_CHROMA_LIMIT) { - return@run cam.chroma * LOW_CHROMA_SCALE - } - return@run null - } } fun getClockPaddingStart(): Int { @@ -430,22 +246,7 @@ private constructor( throw Exception("Cannot find id of $name from $TAG") } - private fun logColor(name: String, color: Int) { - if (DEBUG_COLOR) { - val cam = Cam.fromInt(color) - val tone = CamUtils.lstarFromInt(color) - logger.i("$name -> (hue: ${cam.hue}, chroma: ${cam.chroma}, tone: $tone)") - } - } - companion object { - private val DEBUG_COLOR = true - private val LOW_CHROMA_LIMIT = 15 - private val LOW_CHROMA_SCALE = 1.5f private val TAG = AssetLoader::class.simpleName!! - - private fun getThemeSeedColor(ctx: Context): Int { - return ctx.resources.getColor(android.R.color.system_palette_key_color_primary_light) - } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt index eedddb28ff89..a8839779c4e9 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt @@ -28,6 +28,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockReactiveSetting +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView @@ -46,7 +47,6 @@ class ComposedDigitalLayerController( val layerControllers = mutableListOf<SimpleClockLayerController>() val dozeState = DefaultClockController.AnimationState(1F) - var isRegionDark = true override val view = FlexClockView(ctx, assets, messageBuffer) @@ -103,10 +103,6 @@ class ComposedDigitalLayerController( view.onZenDataChanged(data) } - override fun onColorPaletteChanged(resources: Resources) {} - - override fun onSeedColorChanged(seedColor: Int?) {} - override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} override var isReactiveTouchInteractionEnabled @@ -116,10 +112,6 @@ class ComposedDigitalLayerController( } } - override fun updateColors() { - view.updateColors(assets, isRegionDark) - } - override val animations = object : ClockAnimations { override fun enter() { @@ -158,9 +150,15 @@ class ComposedDigitalLayerController( refreshTime() } - override fun onRegionDarknessChanged(isRegionDark: Boolean) { - this@ComposedDigitalLayerController.isRegionDark = isRegionDark - updateColors() + override fun onThemeChanged(theme: ThemeConfig) { + val color = + when { + theme.seedColor != null -> theme.seedColor!! + theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) + else -> resources.getColor(android.R.color.system_accent2_600) + } + + view.updateColor(color) } override fun onFontSettingChanged(fontSizePx: Float) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 5ed11ad345ad..c5b751820ebd 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -37,6 +37,7 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.DefaultClockFaceLayout +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import java.io.PrintWriter @@ -100,28 +101,33 @@ class DefaultClockController( events.onLocaleChanged(Locale.getDefault()) } - override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { + override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { largeClock.recomputePadding(null) + largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction) smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction) - events.onColorPaletteChanged(resources) + + val theme = ThemeConfig(isDarkTheme, settings?.seedColor) + largeClock.events.onThemeChanged(theme) + smallClock.events.onThemeChanged(theme) + events.onTimeZoneChanged(TimeZone.getDefault()) + smallClock.events.onTimeTick() largeClock.events.onTimeTick() } open inner class DefaultClockFaceController( override val view: AnimatableClockView, - var seedColor: Int?, + seedColor: Int?, messageBuffer: MessageBuffer?, ) : ClockFaceController { - // MAGENTA is a placeholder, and will be assigned correctly in initialize - private var currentColor = Color.MAGENTA - private var isRegionDark = false + private var currentColor = seedColor ?: Color.MAGENTA protected var targetRegion: Rect? = null override val config = ClockFaceConfig() + override var theme = ThemeConfig(true, seedColor) override val layout = DefaultClockFaceLayout(view).apply { views[0].id = @@ -132,9 +138,6 @@ class DefaultClockController( internal set init { - if (seedColor != null) { - currentColor = seedColor!! - } view.setColors(DOZE_COLOR, currentColor) messageBuffer?.let { view.messageBuffer = it } } @@ -143,9 +146,26 @@ class DefaultClockController( object : ClockFaceEvents { override fun onTimeTick() = view.refreshTime() - override fun onRegionDarknessChanged(isRegionDark: Boolean) { - this@DefaultClockFaceController.isRegionDark = isRegionDark - updateColor() + override fun onThemeChanged(theme: ThemeConfig) { + this@DefaultClockFaceController.theme = theme + + val color = + when { + theme.seedColor != null -> theme.seedColor!! + theme.isDarkTheme -> + resources.getColor(android.R.color.system_accent1_100) + else -> resources.getColor(android.R.color.system_accent2_600) + } + + if (currentColor == color) { + return + } + + currentColor = color + view.setColors(DOZE_COLOR, color) + if (!animations.dozeState.isActive) { + view.animateColorChange() + } } override fun onTargetRegionChanged(targetRegion: Rect?) { @@ -165,27 +185,6 @@ class DefaultClockController( } open fun recomputePadding(targetRegion: Rect?) {} - - fun updateColor() { - val color = - if (seedColor != null) { - seedColor!! - } else if (isRegionDark) { - resources.getColor(android.R.color.system_accent1_100) - } else { - resources.getColor(android.R.color.system_accent2_600) - } - - if (currentColor == color) { - return - } - - currentColor = color - view.setColors(DOZE_COLOR, color) - if (!animations.dozeState.isActive) { - view.animateColorChange() - } - } } inner class LargeClockFaceController( @@ -248,19 +247,6 @@ class DefaultClockController( override fun onTimeZoneChanged(timeZone: TimeZone) = clocks.forEach { it.onTimeZoneChanged(timeZone) } - override fun onColorPaletteChanged(resources: Resources) { - largeClock.updateColor() - smallClock.updateColor() - } - - override fun onSeedColorChanged(seedColor: Int?) { - largeClock.seedColor = seedColor - smallClock.seedColor = seedColor - - largeClock.updateColor() - smallClock.updateColor() - } - override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 900971bc3927..8e838f396b64 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -39,7 +39,7 @@ class DefaultClockProvider( val resources: Resources, private val hasStepClockAnimation: Boolean = false, private val migratedClocks: Boolean = false, - private val clockReactiveVariants: Boolean = false, + private val isClockReactiveVariantsEnabled: Boolean = false, ) : ClockProvider { private var messageBuffers: ClockMessageBuffers? = null @@ -54,10 +54,11 @@ class DefaultClockProvider( throw IllegalArgumentException("${settings.clockId} is unsupported by $TAG") } - return if (clockReactiveVariants) { + return if (isClockReactiveVariantsEnabled) { val buffer = messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) + assets.setSeedColor(settings.seedColor, null) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) } else { DefaultClockController( @@ -84,7 +85,7 @@ class DefaultClockProvider( // TODO(b/352049256): Update placeholder to actual resource resources.getDrawable(R.drawable.clock_default_thumbnail, null), isReactiveToTone = true, - isReactiveToTouch = clockReactiveVariants, + isReactiveToTouch = isClockReactiveVariantsEnabled, axes = listOf(), // TODO: Ater some picker definition ) } @@ -103,7 +104,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.FIRST_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -112,7 +112,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -131,7 +130,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.SECOND_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -140,7 +138,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -159,7 +156,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.FIRST_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -168,7 +164,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -187,7 +182,6 @@ class DefaultClockProvider( timespec = DigitalTimespec.SECOND_DIGIT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", lineHeight = 147.25f, fontVariation = "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100", @@ -196,7 +190,6 @@ class DefaultClockProvider( FontTextStyle( fontVariation = "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100", - fontFamily = "google_sans_flex.ttf", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", renderType = RenderType.CHANGE_WEIGHT, @@ -221,13 +214,11 @@ class DefaultClockProvider( timespec = DigitalTimespec.TIME_FULL_FORMAT, style = FontTextStyle( - fontFamily = "google_sans_flex.ttf", fontVariation = "'wght' 600, 'wdth' 100, 'opsz' 144, 'ROND' 100", fontSizeScale = 0.98f, ), aodStyle = FontTextStyle( - fontFamily = "google_sans_flex.ttf", fontVariation = "'wght' 133, 'wdth' 43, 'opsz' 144, 'ROND' 100", fillColorLight = "#FFFFFFFF", outlineColor = "#00000000", diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt index b8ebd0ff559b..a75022a40422 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt @@ -25,6 +25,7 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView @@ -97,24 +98,6 @@ class FlexClockController( largeClock.events.onLocaleChanged(locale) } - override fun onColorPaletteChanged(resources: Resources) { - assets.refreshColorPalette(design.colorPalette) - smallClock.assets.refreshColorPalette(design.colorPalette) - largeClock.assets.refreshColorPalette(design.colorPalette) - - smallClock.events.onColorPaletteChanged(resources) - largeClock.events.onColorPaletteChanged(resources) - } - - override fun onSeedColorChanged(seedColor: Int?) { - assets.setSeedColor(seedColor, design.colorPalette) - smallClock.assets.setSeedColor(seedColor, design.colorPalette) - largeClock.assets.setSeedColor(seedColor, design.colorPalette) - - smallClock.events.onSeedColorChanged(seedColor) - largeClock.events.onSeedColorChanged(seedColor) - } - override fun onWeatherDataChanged(data: WeatherData) { smallClock.events.onWeatherDataChanged(data) largeClock.events.onWeatherDataChanged(data) @@ -136,14 +119,21 @@ class FlexClockController( } } - override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { - events.onColorPaletteChanged(resources) - smallClock.animations.doze(dozeFraction) - largeClock.animations.doze(dozeFraction) - smallClock.animations.fold(foldFraction) - largeClock.animations.fold(foldFraction) - smallClock.events.onTimeTick() - largeClock.events.onTimeTick() + override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { + val theme = ThemeConfig(isDarkTheme, assets.seedColor) + smallClock.run { + events.onThemeChanged(theme) + animations.doze(dozeFraction) + animations.fold(foldFraction) + events.onTimeTick() + } + + largeClock.run { + events.onThemeChanged(theme) + animations.doze(dozeFraction) + animations.fold(foldFraction) + events.onTimeTick() + } } override fun dump(pw: PrintWriter) {} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt index 9067fb094634..8ffc00de5fe5 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt @@ -34,6 +34,7 @@ import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockFaceLayout import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.DefaultClockFaceLayout +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView @@ -59,6 +60,8 @@ class FlexClockFaceController( hasCustomPositionUpdatedAnimation = false // TODO(b/364673982) ) + override var theme = ThemeConfig(true, assets.seedColor) + private val keyguardLargeClockTopMargin = resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin) val layerController: SimpleClockLayerController @@ -135,18 +138,8 @@ class FlexClockFaceController( layerController.faceEvents.onFontSettingChanged(fontSizePx) } - override fun onColorPaletteChanged(resources: Resources) { - layerController.events.onColorPaletteChanged(resources) - layerController.updateColors() - } - - override fun onSeedColorChanged(seedColor: Int?) { - layerController.events.onSeedColorChanged(seedColor) - layerController.updateColors() - } - - override fun onRegionDarknessChanged(isRegionDark: Boolean) { - layerController.faceEvents.onRegionDarknessChanged(isRegionDark) + override fun onThemeChanged(theme: ThemeConfig) { + layerController.faceEvents.onThemeChanged(theme) } override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt index 5d1a2dbc4209..af00cc264208 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt @@ -31,8 +31,4 @@ interface SimpleClockLayerController { val config: ClockFaceConfig @VisibleForTesting var fakeTimeMills: Long? - - // Called immediately after either onColorPaletteChanged or onSeedColorChanged is called. - // Provided for convience to not duplicate color update logic after state updated. - fun updateColors() {} } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt index ce1eae48546a..7b1960efa1c6 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt @@ -32,6 +32,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockReactiveSetting +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.SimpleDigitalClockView @@ -63,7 +64,6 @@ open class SimpleDigitalHandLayerController<T>( override val config = ClockFaceConfig() var dozeState: DefaultClockController.AnimationState? = null - var isRegionDark: Boolean = true init { view.layoutParams = @@ -240,10 +240,6 @@ open class SimpleDigitalHandLayerController<T>( refreshTime() } - override fun onColorPaletteChanged(resources: Resources) {} - - override fun onSeedColorChanged(seedColor: Int?) {} - override fun onWeatherDataChanged(data: WeatherData) {} override fun onAlarmDataChanged(data: AlarmData) {} @@ -253,11 +249,6 @@ open class SimpleDigitalHandLayerController<T>( override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} } - override fun updateColors() { - view.updateColors(assets, isRegionDark) - refreshTime() - } - override val animations = object : ClockAnimations { override fun enter() { @@ -309,21 +300,20 @@ open class SimpleDigitalHandLayerController<T>( applyMargin() } - override fun onRegionDarknessChanged(isRegionDark: Boolean) { - this@SimpleDigitalHandLayerController.isRegionDark = isRegionDark - updateColors() + override fun onThemeChanged(theme: ThemeConfig) { + val color = + when { + theme.seedColor != null -> theme.seedColor!! + theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) + else -> resources.getColor(android.R.color.system_accent2_600) + } + + view.updateColor(color) + refreshTime() } override fun onTargetRegionChanged(targetRegion: Rect?) {} override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {} } - - companion object { - private val DEFAULT_LIGHT_COLOR = "@android:color/system_accent1_100+0" - private val DEFAULT_DARK_COLOR = "@android:color/system_accent2_600+0" - - fun getDefaultColor(assets: AssetLoader, isRegionDark: Boolean) = - assets.readColor(if (isRegionDark) DEFAULT_LIGHT_COLOR else DEFAULT_DARK_COLOR) - } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt index 81efcb9de4d8..ce4d71cff963 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt @@ -27,7 +27,6 @@ import com.android.systemui.log.core.MessageBuffer import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData -import com.android.systemui.shared.clocks.AssetLoader import com.android.systemui.shared.clocks.LogUtil import java.util.Locale @@ -122,8 +121,8 @@ abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) open fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {} - fun updateColors(assets: AssetLoader, isRegionDark: Boolean) { - digitalClockTextViewMap.forEach { _, view -> view.updateColors(assets, isRegionDark) } + fun updateColor(color: Int) { + digitalClockTextViewMap.forEach { _, view -> view.updateColor(color) } invalidate() } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt index 74617b1c0c5c..baed3ae5c7b2 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt @@ -50,7 +50,6 @@ import com.android.systemui.shared.clocks.LogUtil import com.android.systemui.shared.clocks.RenderType import com.android.systemui.shared.clocks.TextStyle import java.lang.Thread -import kotlin.math.ceil import kotlin.math.max import kotlin.math.min @@ -94,11 +93,6 @@ open class SimpleDigitalClockTextView( private var aodDozingInterpolator: Interpolator? = null @VisibleForTesting lateinit var textAnimator: TextAnimator - @VisibleForTesting var outlineAnimator: TextAnimator? = null - // used for hollow style for AOD version - // because stroke style for some fonts have some unwanted inner strokes - // we want to draw this layer on top to oclude them - @VisibleForTesting var innerAnimator: TextAnimator? = null lateinit var typefaceCache: TypefaceVariantCache private set @@ -108,8 +102,6 @@ open class SimpleDigitalClockTextView( if (this::textAnimator.isInitialized) { textAnimator.typefaceCache = value } - outlineAnimator?.typefaceCache = value - innerAnimator?.typefaceCache = value } @VisibleForTesting @@ -136,32 +128,14 @@ open class SimpleDigitalClockTextView( set(value) = super.setText(value) var textBorderWidth = 0F - var aodBorderWidth = 0F var baselineFromMeasure = 0 + var lockscreenColor = Color.WHITE - var textFillColor: Int? = null - var textOutlineColor = TEXT_OUTLINE_DEFAULT_COLOR - var aodFillColor = AOD_DEFAULT_COLOR - var aodOutlineColor = AOD_OUTLINE_DEFAULT_COLOR - - override fun updateColors(assets: AssetLoader, isRegionDark: Boolean) { - val fillColor = if (isRegionDark) textStyle.fillColorLight else textStyle.fillColorDark - textFillColor = - fillColor?.let { assets.readColor(it) } - ?: assets.seedColor - ?: getDefaultColor(assets, isRegionDark) - // for NumberOverlapView to read correct color - lockScreenPaint.color = textFillColor as Int - textStyle.outlineColor?.let { textOutlineColor = assets.readColor(it) } - ?: run { textOutlineColor = TEXT_OUTLINE_DEFAULT_COLOR } - (aodStyle.fillColorLight ?: aodStyle.fillColorDark)?.let { - aodFillColor = assets.readColor(it) - } ?: run { aodFillColor = AOD_DEFAULT_COLOR } - aodStyle.outlineColor?.let { aodOutlineColor = assets.readColor(it) } - ?: run { aodOutlineColor = AOD_OUTLINE_DEFAULT_COLOR } + override fun updateColor(color: Int) { + lockscreenColor = color + lockScreenPaint.color = lockscreenColor if (dozeFraction < 1f) { - textAnimator.setTextStyle(color = textFillColor, animate = false) - outlineAnimator?.setTextStyle(color = textOutlineColor, animate = false) + textAnimator.setTextStyle(color = lockscreenColor, animate = false) } invalidate() } @@ -183,13 +157,9 @@ open class SimpleDigitalClockTextView( if (layout != null) { if (!this::textAnimator.isInitialized) { textAnimator = textAnimatorFactory(layout, ::invalidate) - outlineAnimator = textAnimatorFactory(layout) {} - innerAnimator = textAnimatorFactory(layout) {} setInterpolatorPaint() } else { textAnimator.updateLayout(layout) - outlineAnimator?.updateLayout(layout) - innerAnimator?.updateLayout(layout) } baselineFromMeasure = layout.getLineBaseline(0) } else { @@ -248,10 +218,7 @@ open class SimpleDigitalClockTextView( canvas.translate(0F, measuredHeight.toFloat()) canvas.rotate(-90F) } - logger.d({ "onDraw(); ls: $str1; aod: $str2;" }) { - str1 = textAnimator.textInterpolator.shapedText - str2 = outlineAnimator?.textInterpolator?.shapedText - } + logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText } val translation = getLocalTranslation() canvas.translate(translation.x.toFloat(), translation.y.toFloat()) digitTranslateAnimator?.let { @@ -266,7 +233,6 @@ open class SimpleDigitalClockTextView( (-translation.y + measuredHeight).toFloat(), null, ) - outlineAnimator?.draw(canvas) canvas.saveLayer( -translation.x.toFloat(), -translation.y.toFloat(), @@ -274,11 +240,8 @@ open class SimpleDigitalClockTextView( (-translation.y + measuredHeight).toFloat(), Paint().also { it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) }, ) - innerAnimator?.draw(canvas) canvas.restore() canvas.restore() - } else if (aodStyle.renderType != RenderType.CHANGE_WEIGHT) { - outlineAnimator?.draw(canvas) } textAnimator.draw(canvas) @@ -309,30 +272,13 @@ open class SimpleDigitalClockTextView( val fvar = if (isDozing) aodStyle.fontVariation else textStyle.fontVariation textAnimator.setTextStyle( animate = isAnimated && isAnimationEnabled, - color = if (isDozing) aodFillColor else textFillColor, + color = if (isDozing) AOD_COLOR else lockscreenColor, textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize, fvar = fvar, duration = aodStyle.transitionDuration, interpolator = aodDozingInterpolator, ) updateTextBoundsForTextAnimator() - outlineAnimator?.setTextStyle( - animate = isAnimated && isAnimationEnabled, - color = if (isDozing) aodOutlineColor else textOutlineColor, - textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize, - fvar = fvar, - strokeWidth = if (isDozing) aodBorderWidth else textBorderWidth, - duration = aodStyle.transitionDuration, - interpolator = aodDozingInterpolator, - ) - innerAnimator?.setTextStyle( - animate = isAnimated && isAnimationEnabled, - color = Color.WHITE, - textSize = if (isDozing) aodFontSizePx else lockScreenPaint.textSize, - fvar = fvar, - duration = aodStyle.transitionDuration, - interpolator = aodDozingInterpolator, - ) } override fun animateCharge() { @@ -345,8 +291,6 @@ open class SimpleDigitalClockTextView( val endFvar = if (dozeFraction == 0F) textStyle.fontVariation else aodStyle.fontVariation val startAnimPhase2 = Runnable { textAnimator.setTextStyle(fvar = endFvar, animate = isAnimationEnabled) - outlineAnimator?.setTextStyle(fvar = endFvar, animate = isAnimationEnabled) - innerAnimator?.setTextStyle(fvar = endFvar, animate = isAnimationEnabled) updateTextBoundsForTextAnimator() } textAnimator.setTextStyle( @@ -354,8 +298,6 @@ open class SimpleDigitalClockTextView( animate = isAnimationEnabled, onAnimationEnd = startAnimPhase2, ) - outlineAnimator?.setTextStyle(fvar = middleFvar, animate = isAnimationEnabled) - innerAnimator?.setTextStyle(fvar = middleFvar, animate = isAnimationEnabled) updateTextBoundsForTextAnimator() } @@ -373,8 +315,6 @@ open class SimpleDigitalClockTextView( requestLayout() } else { textAnimator.updateLayout(layout) - outlineAnimator?.updateLayout(layout) - innerAnimator?.updateLayout(layout) } } @@ -500,8 +440,7 @@ open class SimpleDigitalClockTextView( this.aodStyle = textStyle.copy() } this.aodStyle.transitionInterpolator?.let { aodDozingInterpolator = it.interpolator } - aodBorderWidth = parser.convert(this.aodStyle.borderWidth ?: DEFAULT_AOD_STROKE_WIDTH) - lockScreenPaint.strokeWidth = ceil(max(textBorderWidth, aodBorderWidth)) + lockScreenPaint.strokeWidth = textBorderWidth measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED) setInterpolatorPaint() recomputeMaxSingleDigitSizes() @@ -524,27 +463,14 @@ open class SimpleDigitalClockTextView( val lastUnconstrainedHeight = textBounds.height() + lockScreenPaint.strokeWidth * 2 fontSizeAdjustFactor = lastUnconstrainedHeight / lastUnconstrainedTextSize } - textStyle.borderWidthScale?.let { - textBorderWidth = fontSizePx * it - if (dozeFraction < 1.0F) { - outlineAnimator?.setTextStyle(strokeWidth = textBorderWidth, animate = false) - } - } - aodStyle.borderWidthScale?.let { - aodBorderWidth = fontSizePx * it - if (dozeFraction > 0.0F) { - outlineAnimator?.setTextStyle(strokeWidth = aodBorderWidth, animate = false) - } - } + textStyle.borderWidthScale?.let { textBorderWidth = fontSizePx * it } - lockScreenPaint.strokeWidth = ceil(max(textBorderWidth, aodBorderWidth)) + lockScreenPaint.strokeWidth = textBorderWidth recomputeMaxSingleDigitSizes() if (this::textAnimator.isInitialized) { textAnimator.setTextStyle(textSize = lockScreenPaint.textSize, animate = false) } - outlineAnimator?.setTextStyle(textSize = lockScreenPaint.textSize, animate = false) - innerAnimator?.setTextStyle(textSize = lockScreenPaint.textSize, animate = false) } private fun recomputeMaxSingleDigitSizes() { @@ -570,42 +496,7 @@ open class SimpleDigitalClockTextView( textAnimator.setTextStyle( fvar = textStyle.fontVariation, textSize = lockScreenPaint.textSize, - color = textFillColor, - animate = false, - ) - } - - if (outlineAnimator != null) { - outlineAnimator!! - .textInterpolator - .targetPaint - .set( - TextPaint(lockScreenPaint).also { - it.style = - if (aodStyle.renderType == RenderType.HOLLOW_TEXT) - Paint.Style.FILL_AND_STROKE - else Paint.Style.STROKE - } - ) - outlineAnimator!!.textInterpolator.onTargetPaintModified() - outlineAnimator!!.setTextStyle( - fvar = aodStyle.fontVariation, - textSize = lockScreenPaint.textSize, - color = Color.TRANSPARENT, - animate = false, - ) - } - - if (innerAnimator != null) { - innerAnimator!! - .textInterpolator - .targetPaint - .set(TextPaint(lockScreenPaint).also { it.style = Paint.Style.FILL }) - innerAnimator!!.textInterpolator.onTargetPaintModified() - innerAnimator!!.setTextStyle( - fvar = aodStyle.fontVariation, - textSize = lockScreenPaint.textSize, - color = Color.WHITE, + color = lockscreenColor, animate = false, ) } @@ -641,14 +532,7 @@ open class SimpleDigitalClockTextView( } companion object { - val DEFAULT_AOD_STROKE_WIDTH = "2dp" - val TEXT_OUTLINE_DEFAULT_COLOR = Color.TRANSPARENT - val AOD_DEFAULT_COLOR = Color.TRANSPARENT - val AOD_OUTLINE_DEFAULT_COLOR = Color.WHITE - private val DEFAULT_LIGHT_COLOR = "@android:color/system_accent1_100+0" - private val DEFAULT_DARK_COLOR = "@android:color/system_accent2_600+0" - - fun getDefaultColor(assets: AssetLoader, isRegionDark: Boolean) = - assets.readColor(if (isRegionDark) DEFAULT_LIGHT_COLOR else DEFAULT_DARK_COLOR) + val AOD_STROKE_WIDTH = "2dp" + val AOD_COLOR = Color.WHITE } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt index bbd2d3d1f782..30b54d881d92 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt @@ -32,7 +32,7 @@ interface SimpleDigitalClockView { fun applyTextSize(targetFontSizePx: Float?, constrainedByHeight: Boolean = false) - fun updateColors(assets: AssetLoader, isRegionDark: Boolean) + fun updateColor(color: Int) fun refreshTime() diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp index 2f1d354c3b3e..afdcae481df5 100644 --- a/packages/SystemUI/log/Android.bp +++ b/packages/SystemUI/log/Android.bp @@ -22,19 +22,15 @@ package { default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], } -android_library { +java_library { name: "SystemUILogLib", - use_resource_processor: true, srcs: [ "src/**/*.java", "src/**/*.kt", ], static_libs: [ - "androidx.core_core-ktx", - "androidx.annotation_annotation", "error_prone_annotations", "SystemUICommon", ], - manifest: "AndroidManifest.xml", kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SystemUI/log/AndroidManifest.xml b/packages/SystemUI/log/AndroidManifest.xml deleted file mode 100644 index 4021e1a5f751..000000000000 --- a/packages/SystemUI/log/AndroidManifest.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2023 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.log"> - - -</manifest> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt index 48f6cc4261a9..14d34d79512f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/ActiveUnlockConfigTest.kt @@ -33,6 +33,7 @@ import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOM import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -47,6 +48,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -55,6 +57,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest +@RunWith(AndroidJUnit4::class) class ActiveUnlockConfigTest : SysuiTestCase() { private lateinit var secureSettings: FakeSettings @Mock private lateinit var contentResolver: ContentResolver diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java index edf29c591af9..b937db6bd76d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java @@ -25,6 +25,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.TextView; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -32,10 +33,12 @@ import com.android.systemui.res.R; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.List; @SmallTest +@RunWith(AndroidJUnit4.class) public class KeyguardClockAccessibilityDelegateTest extends SysuiTestCase { private TextView mView; @@ -111,4 +114,4 @@ public class KeyguardClockAccessibilityDelegateTest extends SysuiTestCase { private boolean isEmpty(List<CharSequence> texts) { return texts.stream().allMatch(TextUtils::isEmpty); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/DependencyTest.java index 006864489170..006864489170 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/DependencyTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java index ccdcee5e0318..1b072416faa6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ExpandHelperTest.java @@ -21,12 +21,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.content.Context; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.core.animation.ObjectAnimator; import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; @@ -42,7 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @RunWithLooper public class ExpandHelperTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/InitControllerTest.java index e0ca87c0223d..e0c7c7a6c16d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/InitControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/InitControllerTest.java @@ -19,16 +19,16 @@ package com.android.systemui; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper public class InitControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java index b08f97a646b9..b08f97a646b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java index ee8ce17cecd4..ee8ce17cecd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt index 37f549a0c675..37f549a0c675 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt new file mode 100644 index 000000000000..75a5768193cf --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/FlingOnBackAnimationCallbackTest.kt @@ -0,0 +1,127 @@ +/* + * 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.animation.back + +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.view.animation.Interpolator +import android.window.BackEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.app.animation.Interpolators +import com.android.systemui.SysuiTestCase +import com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito + +@SmallTest +@RunWith(AndroidJUnit4::class) +class FlingOnBackAnimationCallbackTest : SysuiTestCase() { + + @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Test + fun testProgressInterpolation() { + val mockInterpolator = Mockito.mock(Interpolator::class.java) + val backEvent = backEventOf(0.5f) + Mockito.`when`(mockInterpolator.getInterpolation(0.5f)).thenReturn(0.8f) + val callback = TestFlingOnBackAnimationCallback(mockInterpolator) + callback.onBackStarted(backEvent) + assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled) + callback.onBackProgressed(backEvent) + assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled) + assertEquals("Assert interpolated progress", 0.8f, callback.progressEvent?.progress) + } + + @Test + @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + fun testFling() { + val callback = TestFlingOnBackAnimationCallback(Interpolators.LINEAR) + callback.onBackStarted(backEventOf(progress = 0f, frameTime = 0)) + assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled) + callback.onBackProgressed(backEventOf(0f, 8)) + callback.onBackProgressed(backEventOf(0.2f, 16)) + callback.onBackProgressed(backEventOf(0.4f, 24)) + callback.onBackProgressed(backEventOf(0.6f, 32)) + assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled) + assertEquals("Assert interpolated progress", 0.6f, callback.progressEvent?.progress) + getInstrumentation().runOnMainSync { callback.onBackInvoked() } + // Assert that onBackInvoked is not called immediately... + assertFalse(callback.backInvokedCalled) + // Instead the fling animation is played and eventually onBackInvoked is called. + callback.backInvokedLatch.await(1000, TimeUnit.MILLISECONDS) + assertTrue(callback.backInvokedCalled) + } + + @Test + @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + fun testCallbackWithoutTimestampApi() { + // Assert that all callback methods are immediately forwarded + val callback = TestFlingOnBackAnimationCallback(Interpolators.LINEAR) + callback.onBackStarted(backEventOf(progress = 0f, frameTime = 0)) + assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled) + callback.onBackProgressed(backEventOf(0f, 8)) + assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled) + callback.onBackInvoked() + assertTrue("Assert onBackInvoked called", callback.backInvokedCalled) + callback.onBackCancelled() + assertTrue("Assert onBackCancelled called", callback.backCancelledCalled) + } + + private fun backEventOf(progress: Float, frameTime: Long = 0): BackEvent { + return BackEvent(10f, 10f, progress, 0, frameTime) + } + + /** Helper class to expose the compat functions for testing */ + private class TestFlingOnBackAnimationCallback(progressInterpolator: Interpolator) : + FlingOnBackAnimationCallback(progressInterpolator) { + var backStartedCalled = false + var backProgressedCalled = false + var backInvokedCalled = false + val backInvokedLatch = CountDownLatch(1) + var backCancelledCalled = false + var progressEvent: BackEvent? = null + + override fun onBackStartedCompat(backEvent: BackEvent) { + backStartedCalled = true + } + + override fun onBackProgressedCompat(backEvent: BackEvent) { + backProgressedCalled = true + progressEvent = backEvent + } + + override fun onBackInvokedCompat() { + backInvokedCalled = true + backInvokedLatch.countDown() + } + + override fun onBackCancelledCompat() { + backCancelledCalled = true + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt index 25b85b514435..9f0c7e1ba660 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegateTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import android.testing.TestableLooper import android.widget.Button import android.widget.TextView @@ -43,7 +43,7 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @OptIn(ExperimentalCoroutinesApi::class) class AudioSharingDialogDelegateTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt index 9c427c6b085e..44f9720cb9e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt @@ -15,7 +15,7 @@ */ package com.android.systemui.bluetooth.qsdialog -import android.testing.AndroidTestingRunner +import androidx.test.ext.junit.runners.AndroidJUnit4 import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -37,7 +37,7 @@ import org.mockito.junit.MockitoRule import org.mockito.kotlin.whenever @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @OptIn(ExperimentalCoroutinesApi::class) class DeviceItemActionInteractorTest : SysuiTestCase() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/ActionReceiverTest.kt index b7ed27f7c71e..94d0dfeccbe1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/ActionReceiverTest.kt @@ -21,8 +21,8 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -51,7 +51,7 @@ import java.lang.IllegalArgumentException import java.lang.IllegalStateException import java.util.concurrent.Executor -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @SmallTest class ActionReceiverTest : SysuiTestCase() { @@ -273,4 +273,4 @@ class ActionReceiverTest : SysuiTestCase() { fun testBroadcastWithWrongAction_throwsException() { actionReceiver.onReceive(mContext, Intent(ACTION2)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/compose/ComposeInitializerTest.kt index c2fe009c13bb..c2fe009c13bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/compose/ComposeInitializerTest.kt 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 f5d2d42902d8..8062358f670c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -96,7 +96,7 @@ import org.mockito.kotlin.firstValue import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times -import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -453,7 +453,7 @@ class DreamOverlayServiceTest : SysuiTestCase() { mMainExecutor.runAllReady() - verifyZeroInteractions(mTouchMonitor) + verifyNoMoreInteractions(mTouchMonitor) val captor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback::class.java) verify(mStateController).addCallback(captor.capture()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt index e7d2ef10b4ee..21679f960198 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt @@ -187,7 +187,7 @@ class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) : signalCount = 1, usageSessionStartTime = secondSignalReceivedTime, userId = 0, - gestureType = gestureType + gestureType = gestureType, ) ) } @@ -402,6 +402,29 @@ class KeyboardTouchpadEduInteractorTest(private val gestureType: GestureType) : eduClock.offset(initialDelayElapsedDuration) } + fun logMetricsForToastEducation() = + testScope.runTest { + triggerMaxEducationSignals(gestureType) + runCurrent() + + verify(kosmos.mockEduMetricsLogger) + .logContextualEducationTriggered(gestureType, EducationUiType.Toast) + } + + @Test + fun logMetricsForNotificationEducation() = + testScope.runTest { + triggerMaxEducationSignals(gestureType) + runCurrent() + + eduClock.offset(minDurationForNextEdu) + triggerMaxEducationSignals(gestureType) + runCurrent() + + verify(kosmos.mockEduMetricsLogger) + .logContextualEducationTriggered(gestureType, EducationUiType.Notification) + } + @After fun clear() { testScope.launch { tutorialSchedulerRepository.clearDataStore() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/globalactions/ShutdownUiTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/globalactions/ShutdownUiTest.java index 73509e2da520..acc17e73ac69 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/globalactions/ShutdownUiTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/globalactions/ShutdownUiTest.java @@ -19,20 +19,25 @@ package com.android.systemui.globalactions; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.nearby.NearbyManager; import android.net.platform.flags.Flags; import android.os.PowerManager; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.testing.TestableLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.BlurUtils; import org.junit.Before; import org.junit.Test; @@ -46,14 +51,13 @@ public class ShutdownUiTest extends SysuiTestCase { ShutdownUi mShutdownUi; @Mock - BlurUtils mBlurUtils; - @Mock NearbyManager mNearbyManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mShutdownUi = new ShutdownUi(getContext(), mBlurUtils, mNearbyManager); + mContext = spy(mContext); + mShutdownUi = new ShutdownUi(mContext, mNearbyManager); } @Test @@ -140,4 +144,24 @@ public class ShutdownUiTest extends SysuiTestCase { assertEquals(actualLayout, expectedLayout); } + /** + * Main looper required here because showShutdown UI creates a dialog which instantiates a + * handler that needs to be on the main thread. + */ + @TestableLooper.RunWithLooper(setAsMainLooper = true) + @Test + public void showShutdownUi_loadsShutdownTextColorAndAlpha() { + this.allowTestableLooperAsMainThread(); + + Resources mockResources = spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mockResources); + + mShutdownUi.showShutdownUi(false, "test"); + + verify(mockResources).getFloat( + eq(com.android.systemui.res.R.dimen.shutdown_scrim_behind_alpha)); + verify(mockResources).getColor( + eq(com.android.systemui.res.R.color.global_actions_shutdown_ui_text), + any()); + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt new file mode 100644 index 000000000000..5efb6171cdde --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModelTest.kt @@ -0,0 +1,166 @@ +/* + * 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.haptics.msdl.qs + +import android.service.quicksettings.Tile +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.haptics.msdl.fakeMSDLPlayer +import com.android.systemui.haptics.msdl.tileHapticsViewModelFactory +import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.panels.ui.viewmodel.fakeQsTile +import com.android.systemui.qs.panels.ui.viewmodel.tileViewModel +import com.android.systemui.testKosmos +import com.google.android.msdl.data.model.MSDLToken +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +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 + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class TileHapticsViewModelTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val qsTile = kosmos.fakeQsTile + private val msdlPlayer = kosmos.fakeMSDLPlayer + private val tileViewModel = kosmos.tileViewModel + + private val underTest = kosmos.tileHapticsViewModelFactory.create(tileViewModel) + + @Before + fun setUp() { + underTest.activateIn(testScope) + } + + @Test + fun whenTileTogglesOnFromClick_playsSwitchOnHaptics() = + testScope.runTest { + // WHEN the tile toggles on after being clicked + underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED) + toggleOn() + + // THEN the switch on token plays + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWITCH_ON) + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun whenTileTogglesOffFromClick_playsSwitchOffHaptics() = + testScope.runTest { + // WHEN the tile toggles off after being clicked + underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED) + toggleOff() + + // THEN the switch off token plays + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWITCH_OFF) + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun whenTileTogglesOnWhileIdle_doesNotPlaySwitchOnHaptics() = + testScope.runTest { + // WHEN the tile toggles on without being clicked + toggleOn() + + // THEN no token plays + assertThat(msdlPlayer.latestTokenPlayed).isNull() + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun whenTileTogglesOffWhileIdle_doesNotPlaySwitchOffHaptics() = + testScope.runTest { + // WHEN the tile toggles off without being clicked + toggleOff() + + // THEN no token plays + assertThat(msdlPlayer.latestTokenPlayed).isNull() + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun whenLaunchingFromLongClick_playsLongPressHaptics() = + testScope.runTest { + // WHEN the tile is long-clicked and its action state changes accordingly + underTest.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.LONG_CLICKED + ) + // WHEN the activity transition (from the long-click) starts + underTest.onActivityLaunchTransitionStart() + runCurrent() + + // THEN the long-press token plays + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.LONG_PRESS) + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun onLongClick_whenTileDoesNotHandleLongClick_playsFailureHaptics() = + testScope.runTest { + // WHEN the tile is long-clicked but the tile does not handle a long-click + val state = QSTile.State().apply { handlesLongClick = false } + qsTile.changeState(state) + underTest.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.LONG_CLICKED + ) + runCurrent() + + // THEN the failure token plays + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE) + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + @Test + fun whenLaunchingFromClick_doesNotPlayHaptics() = + testScope.runTest { + // WHEN the tile is clicked and its action state changes accordingly + underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED) + // WHEN an activity transition starts (from clicking) + underTest.onActivityLaunchTransitionStart() + runCurrent() + + // THEN no haptics play + assertThat(msdlPlayer.latestTokenPlayed).isNull() + assertThat(msdlPlayer.latestPropertiesPlayed).isNull() + } + + private fun TestScope.toggleOn() { + qsTile.changeState(QSTile.State().apply { state = Tile.STATE_INACTIVE }) + runCurrent() + + qsTile.changeState(QSTile.State().apply { state = Tile.STATE_ACTIVE }) + runCurrent() + } + + private fun TestScope.toggleOff() { + qsTile.changeState(QSTile.State().apply { state = Tile.STATE_ACTIVE }) + runCurrent() + + qsTile.changeState(QSTile.State().apply { state = Tile.STATE_INACTIVE }) + runCurrent() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt index 945f95385db2..38e4ae101d06 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt @@ -23,11 +23,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository +import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.res.R +import com.android.systemui.settings.userTracker import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.hours @@ -75,16 +77,22 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { TutorialSchedulerRepository( context, dataStoreScope, - dataStoreName = "TutorialNotificationCoordinatorTest" + dataStoreName = "TutorialNotificationCoordinatorTest", ) val interactor = - TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, repository) + TutorialSchedulerInteractor( + keyboardRepository, + touchpadRepository, + repository, + kosmos.inputDeviceTutorialLogger, + ) underTest = TutorialNotificationCoordinator( testScope.backgroundScope, context, interactor, - notificationManager + notificationManager, + kosmos.userTracker, ) notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) underTest.start() @@ -103,7 +111,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_tutorial_notification_title, - R.string.launch_keyboard_tutorial_notification_content + R.string.launch_keyboard_tutorial_notification_content, ) } @@ -114,7 +122,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_touchpad_tutorial_notification_title, - R.string.launch_touchpad_tutorial_notification_content + R.string.launch_touchpad_tutorial_notification_content, ) } @@ -126,7 +134,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_touchpad_tutorial_notification_title, - R.string.launch_keyboard_touchpad_tutorial_notification_content + R.string.launch_keyboard_touchpad_tutorial_notification_content, ) } @@ -134,12 +142,13 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { fun doNotShowNotification() = testScope.runTest { advanceTimeBy(LAUNCH_DELAY) - verify(notificationManager, never()).notify(eq(TAG), eq(NOTIFICATION_ID), any()) + verify(notificationManager, never()) + .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any()) } private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) { verify(notificationManager) - .notify(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture()) + .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any()) val notification = notificationCaptor.value val actualTitle = notification.getString(Notification.EXTRA_TITLE) val actualContent = notification.getString(Notification.EXTRA_TEXT) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt index 650f9dc7f104..b0ffc47cbc6c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType +import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope @@ -62,10 +63,15 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { TutorialSchedulerRepository( context, dataStoreScope, - dataStoreName = "TutorialSchedulerInteractorTest" + dataStoreName = "TutorialSchedulerInteractorTest", ) underTest = - TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, schedulerRepository) + TutorialSchedulerInteractor( + keyboardRepository, + touchpadRepository, + schedulerRepository, + kosmos.inputDeviceTutorialLogger, + ) } @After diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt index 6e883c24b5d2..9e20e7d0f98c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyboard.shortcut.data.repository import android.hardware.input.fakeInputManager +import android.view.KeyEvent.KEYCODE_1 import android.view.KeyEvent.KEYCODE_A import android.view.KeyEvent.KEYCODE_B import android.view.KeyEvent.KEYCODE_C @@ -24,6 +25,7 @@ import android.view.KeyEvent.KEYCODE_D import android.view.KeyEvent.KEYCODE_E import android.view.KeyEvent.KEYCODE_F import android.view.KeyEvent.KEYCODE_G +import android.view.KeyEvent.META_FUNCTION_ON import android.view.KeyboardShortcutGroup import android.view.KeyboardShortcutInfo import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -87,6 +89,48 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { } @Test + fun categories_keycodeAndModifiersAreMappedSeparatelyWhenIdentical() = + testScope.runTest { + fakeSystemSource.setGroups(simpleGroup(simpleShortcutInfo(KEYCODE_1))) + + helper.toggle(deviceId = 123) + val categories by collectLastValue(repo.categories) + + val systemCategory = categories?.firstOrNull { it.type == ShortcutCategoryType.System } + + // Keycode 0x8 should be translated to the Key 1 instead of modifier FN + // which has the same keycode. + val expectedCategory = + ShortcutCategory( + type = ShortcutCategoryType.System, + simpleSubCategory(simpleShortcut("1")), + ) + + assertThat(systemCategory).isEqualTo(expectedCategory) + } + + @Test + fun categories_keyCodeAndModifierHaveSameCode_codesAreMappedCorrectly() = + testScope.runTest { + fakeSystemSource.setGroups(simpleGroup(simpleShortcutInfo(KEYCODE_1, META_FUNCTION_ON))) + + helper.toggle(deviceId = 123) + val categories by collectLastValue(repo.categories) + + val systemCategory = categories?.firstOrNull { it.type == ShortcutCategoryType.System } + + // Keycode 0x8 should be translated to the Key 1 instead of modifier FN + // which has the same keycode. while modifier mask 0x8 should be translated to FN. + val expectedCategory = + ShortcutCategory( + type = ShortcutCategoryType.System, + simpleSubCategory(simpleShortcut("Fn", "1")), + ) + + assertThat(systemCategory).isEqualTo(expectedCategory) + } + + @Test fun categories_multipleSubscribers_replaysExistingValueToNewSubscribers() = testScope.runTest { fakeSystemSource.setGroups(TestShortcuts.systemGroups) @@ -111,24 +155,14 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { testScope.runTest { fakeSystemSource.setGroups( listOf( - simpleGroup( - simpleShortcutInfo(KEYCODE_A), - simpleShortcutInfo(KEYCODE_B), - ), - simpleGroup( - simpleShortcutInfo(KEYCODE_C), - ), + simpleGroup(simpleShortcutInfo(KEYCODE_A), simpleShortcutInfo(KEYCODE_B)), + simpleGroup(simpleShortcutInfo(KEYCODE_C)), ) ) fakeMultiTaskingSource.setGroups( listOf( - simpleGroup( - simpleShortcutInfo(KEYCODE_D), - ), - simpleGroup( - simpleShortcutInfo(KEYCODE_E), - simpleShortcutInfo(KEYCODE_F), - ), + simpleGroup(simpleShortcutInfo(KEYCODE_D)), + simpleGroup(simpleShortcutInfo(KEYCODE_E), simpleShortcutInfo(KEYCODE_F)), ) ) fakeAppCategoriesSource.setGroups(listOf(simpleGroup(simpleShortcutInfo(KEYCODE_G)))) @@ -144,16 +178,11 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { listOf( simpleSubCategory(simpleShortcut("B")), simpleSubCategory(simpleShortcut("C")), - ) + ), ), ShortcutCategory( ShortcutCategoryType.MultiTasking, - listOf( - simpleSubCategory( - simpleShortcut("E"), - simpleShortcut("F"), - ), - ) + listOf(simpleSubCategory(simpleShortcut("E"), simpleShortcut("F"))), ), ) } @@ -164,14 +193,14 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { private fun simpleShortcut(vararg keys: String) = Shortcut( label = simpleShortcutLabel, - commands = listOf(ShortcutCommand(keys.map { ShortcutKey.Text(it) })) + commands = listOf(ShortcutCommand(keys.map { ShortcutKey.Text(it) })), ) private fun simpleGroup(vararg shortcuts: KeyboardShortcutInfo) = KeyboardShortcutGroup(simpleGroupLabel, shortcuts.asList()) - private fun simpleShortcutInfo(keyCode: Int = 0) = - KeyboardShortcutInfo(simpleShortcutLabel, keyCode, /* modifiers= */ 0) + private fun simpleShortcutInfo(keyCode: Int = 0, modifiers: Int = 0) = + KeyboardShortcutInfo(simpleShortcutLabel, keyCode, modifiers) private val simpleShortcutLabel = "shortcut label" private val simpleGroupLabel = "group label" diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt index 972ca02548ec..972ca02548ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index bfe89de6229d..bfe89de6229d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index eef4c3de4b19..83d2617fd343 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -20,7 +20,6 @@ package com.android.systemui.keyguard.domain.interactor import android.app.admin.DevicePolicyManager import android.content.Intent import android.os.UserHandle -import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.keyguard.logging.KeyguardQuickAffordancesLogger @@ -79,10 +78,6 @@ import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) -@FlakyTest( - bugId = 292574995, - detail = "on certain architectures all permutations with startActivity=true is causing failures" -) @SmallTest @RunWith(ParameterizedAndroidJunit4::class) @DisableSceneContainer @@ -93,11 +88,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { private val DRAWABLE = mock<Icon> { whenever(this.contentDescription) - .thenReturn( - ContentDescription.Resource( - res = CONTENT_DESCRIPTION_RESOURCE_ID, - ) - ) + .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) } private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337 @@ -273,13 +264,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { context = context, userFileManager = mock<UserFileManager>().apply { - whenever( - getSharedPreferences( - anyString(), - anyInt(), - anyInt(), - ) - ) + whenever(getSharedPreferences(anyString(), anyInt(), anyInt())) .thenReturn(FakeSharedPreferences()) }, userTracker = userTracker, @@ -316,9 +301,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = - KeyguardInteractorFactory.create( - featureFlags = featureFlags, - ) + KeyguardInteractorFactory.create(featureFlags = featureFlags) .keyguardInteractor, shadeInteractor = kosmos.shadeInteractor, lockPatternUtils = lockPatternUtils, @@ -350,9 +333,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { homeControls.setState( lockScreenState = - KeyguardQuickAffordanceConfig.LockScreenState.Visible( - icon = DRAWABLE, - ) + KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE) ) homeControls.onTriggeredResult = if (startActivity) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt index ad5eeabf83d2..ad5eeabf83d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt index ecc62e908a4f..ecc62e908a4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt index 0c3fcb3ef759..0c3fcb3ef759 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt index eae6cdbe4d2c..d921dde1fa44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt @@ -35,44 +35,30 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class TaskbarDelegateTest : SysuiTestCase() { - val DISPLAY_ID = 0; - val MODE_GESTURE = 0; - val MODE_THREE_BUTTON = 1; + val DISPLAY_ID = 0 + + val MODE_GESTURE = 0 + + val MODE_THREE_BUTTON = 1 private lateinit var mTaskStackChangeListeners: TaskStackChangeListeners private lateinit var mTaskbarDelegate: TaskbarDelegate - @Mock - lateinit var mEdgeBackGestureHandler : EdgeBackGestureHandler - @Mock - lateinit var mLightBarControllerFactory : LightBarTransitionsController.Factory - @Mock - lateinit var mLightBarTransitionController: LightBarTransitionsController - @Mock - lateinit var mCommandQueue: CommandQueue - @Mock - lateinit var mOverviewProxyService: OverviewProxyService - @Mock - lateinit var mNavBarHelper: NavBarHelper - @Mock - lateinit var mNavigationModeController: NavigationModeController - @Mock - lateinit var mSysUiState: SysUiState - @Mock - lateinit var mDumpManager: DumpManager - @Mock - lateinit var mAutoHideController: AutoHideController - @Mock - lateinit var mLightBarController: LightBarController - @Mock - lateinit var mOptionalPip: Optional<Pip> - @Mock - lateinit var mBackAnimation: BackAnimation - @Mock - lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState - @Mock - lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager - @Mock - lateinit var mStatusBarStateController: StatusBarStateController + @Mock lateinit var mEdgeBackGestureHandler: EdgeBackGestureHandler + @Mock lateinit var mLightBarControllerFactory: LightBarTransitionsController.Factory + @Mock lateinit var mLightBarTransitionController: LightBarTransitionsController + @Mock lateinit var mCommandQueue: CommandQueue + @Mock lateinit var mOverviewProxyService: OverviewProxyService + @Mock lateinit var mNavBarHelper: NavBarHelper + @Mock lateinit var mNavigationModeController: NavigationModeController + @Mock lateinit var mSysUiState: SysUiState + @Mock lateinit var mDumpManager: DumpManager + @Mock lateinit var mAutoHideController: AutoHideController + @Mock lateinit var mLightBarController: LightBarController + @Mock lateinit var mOptionalPip: Optional<Pip> + @Mock lateinit var mBackAnimation: BackAnimation + @Mock lateinit var mCurrentSysUiState: NavBarHelper.CurrentSysuiState + @Mock lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock lateinit var mStatusBarStateController: StatusBarStateController @Before fun setup() { @@ -82,11 +68,26 @@ class TaskbarDelegateTest : SysuiTestCase() { `when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState) `when`(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState) mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance() - mTaskbarDelegate = TaskbarDelegate(context, mLightBarControllerFactory, - mStatusBarKeyguardViewManager, mStatusBarStateController) - mTaskbarDelegate.setDependencies(mCommandQueue, mOverviewProxyService, mNavBarHelper, - mNavigationModeController, mSysUiState, mDumpManager, mAutoHideController, - mLightBarController, mOptionalPip, mBackAnimation, mTaskStackChangeListeners) + mTaskbarDelegate = + TaskbarDelegate( + context, + mLightBarControllerFactory, + mStatusBarKeyguardViewManager, + mStatusBarStateController, + ) + mTaskbarDelegate.setDependencies( + mCommandQueue, + mOverviewProxyService, + mNavBarHelper, + mNavigationModeController, + mSysUiState, + mDumpManager, + mAutoHideController, + mLightBarController, + mOptionalPip, + mBackAnimation, + mTaskStackChangeListeners, + ) } @Test @@ -108,10 +109,12 @@ class TaskbarDelegateTest : SysuiTestCase() { fun screenPinningEnabled_updatesSysuiState() { mTaskbarDelegate.init(DISPLAY_ID) mTaskStackChangeListeners.listenerImpl.onLockTaskModeChanged( - ActivityManager.LOCK_TASK_MODE_PINNED) - verify(mSysUiState, times(1)).setFlag( - ArgumentMatchers.eq(QuickStepContract.SYSUI_STATE_SCREEN_PINNING), - ArgumentMatchers.eq(true) + ActivityManager.LOCK_TASK_MODE_PINNED ) + verify(mSysUiState, times(1)) + .setFlag( + ArgumentMatchers.eq(QuickStepContract.SYSUI_STATE_SCREEN_PINNING), + ArgumentMatchers.eq(true), + ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt index f3cea3e8bb96..a64eda7af790 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt @@ -8,24 +8,23 @@ import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalcul import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position import com.google.common.truth.Truth.assertThat import org.junit.Test +import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 -import platform.test.runner.parameterized.Parameter import platform.test.runner.parameterized.Parameters -import org.junit.runner.RunWith @RunWith(ParameterizedAndroidJunit4::class) @SmallTest -internal class FloatingRotationButtonPositionCalculatorTest( - private val testCase: TestCase, -) : SysuiTestCase() { +internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase) : + SysuiTestCase() { @Test fun calculatePosition() { - val position = testCase.calculator.calculatePosition( - testCase.rotation, - testCase.taskbarVisible, - testCase.taskbarStashed - ) + val position = + testCase.calculator.calculatePosition( + testCase.rotation, + testCase.taskbarVisible, + testCase.taskbarStashed, + ) assertThat(position).isEqualTo(testCase.expectedPosition) } @@ -34,21 +33,22 @@ internal class FloatingRotationButtonPositionCalculatorTest( val rotation: Int, val taskbarVisible: Boolean, val taskbarStashed: Boolean, - val expectedPosition: Position + val expectedPosition: Position, ) { - override fun toString(): String = - buildString { - append("when calculator = ") - append(when (calculator) { - posLeftCalculator -> "LEFT" - posRightCalculator -> "RIGHT" - else -> error("Unknown calculator: $calculator") - }) - append(", rotation = $rotation") - append(", taskbarVisible = $taskbarVisible") - append(", taskbarStashed = $taskbarStashed") - append(" - expected $expectedPosition") + override fun toString(): String = buildString { + append("when calculator = ") + append( + when (calculator) { + posLeftCalculator -> "LEFT" + posRightCalculator -> "RIGHT" + else -> error("Unknown calculator: $calculator") } + ) + append(", rotation = $rotation") + append(", taskbarVisible = $taskbarVisible") + append(", taskbarStashed = $taskbarStashed") + append(" - expected $expectedPosition") + } } companion object { @@ -56,12 +56,20 @@ internal class FloatingRotationButtonPositionCalculatorTest( private const val MARGIN_TASKBAR_LEFT = 20 private const val MARGIN_TASKBAR_BOTTOM = 30 - private val posLeftCalculator = FloatingRotationButtonPositionCalculator( - MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM, true - ) - private val posRightCalculator = FloatingRotationButtonPositionCalculator( - MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM, false - ) + private val posLeftCalculator = + FloatingRotationButtonPositionCalculator( + MARGIN_DEFAULT, + MARGIN_TASKBAR_LEFT, + MARGIN_TASKBAR_BOTTOM, + true, + ) + private val posRightCalculator = + FloatingRotationButtonPositionCalculator( + MARGIN_DEFAULT, + MARGIN_TASKBAR_LEFT, + MARGIN_TASKBAR_BOTTOM, + false, + ) @Parameters(name = "{0}") @JvmStatic @@ -73,77 +81,84 @@ internal class FloatingRotationButtonPositionCalculatorTest( rotation = Surface.ROTATION_0, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.LEFT, - translationX = MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_90, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.RIGHT, - translationX = -MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_180, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.TOP or Gravity.RIGHT, - translationX = -MARGIN_DEFAULT, - translationY = MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.TOP or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_270, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.TOP or Gravity.LEFT, - translationX = MARGIN_DEFAULT, - translationY = MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.TOP or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_0, taskbarVisible = true, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.LEFT, - translationX = MARGIN_TASKBAR_LEFT, - translationY = -MARGIN_TASKBAR_BOTTOM - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_TASKBAR_LEFT, + translationY = -MARGIN_TASKBAR_BOTTOM, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_0, taskbarVisible = true, taskbarStashed = true, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.LEFT, - translationX = MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posLeftCalculator, rotation = Surface.ROTATION_90, taskbarVisible = true, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.RIGHT, - translationX = -MARGIN_TASKBAR_LEFT, - translationY = -MARGIN_TASKBAR_BOTTOM - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_TASKBAR_LEFT, + translationY = -MARGIN_TASKBAR_BOTTOM, + ), ), // Position right @@ -152,78 +167,85 @@ internal class FloatingRotationButtonPositionCalculatorTest( rotation = Surface.ROTATION_0, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.RIGHT, - translationX = -MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_90, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.TOP or Gravity.RIGHT, - translationX = -MARGIN_DEFAULT, - translationY = MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.TOP or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_180, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.TOP or Gravity.LEFT, - translationX = MARGIN_DEFAULT, - translationY = MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.TOP or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = MARGIN_DEFAULT, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_270, taskbarVisible = false, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.LEFT, - translationX = MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.LEFT, + translationX = MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_0, taskbarVisible = true, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.RIGHT, - translationX = -MARGIN_TASKBAR_LEFT, - translationY = -MARGIN_TASKBAR_BOTTOM - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_TASKBAR_LEFT, + translationY = -MARGIN_TASKBAR_BOTTOM, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_0, taskbarVisible = true, taskbarStashed = true, - expectedPosition = Position( - gravity = Gravity.BOTTOM or Gravity.RIGHT, - translationX = -MARGIN_DEFAULT, - translationY = -MARGIN_DEFAULT - ) + expectedPosition = + Position( + gravity = Gravity.BOTTOM or Gravity.RIGHT, + translationX = -MARGIN_DEFAULT, + translationY = -MARGIN_DEFAULT, + ), ), TestCase( calculator = posRightCalculator, rotation = Surface.ROTATION_90, taskbarVisible = true, taskbarStashed = false, - expectedPosition = Position( - gravity = Gravity.TOP or Gravity.RIGHT, - translationX = -MARGIN_TASKBAR_LEFT, - translationY = MARGIN_TASKBAR_BOTTOM - ) - ) + expectedPosition = + Position( + gravity = Gravity.TOP or Gravity.RIGHT, + translationX = -MARGIN_TASKBAR_LEFT, + translationY = MARGIN_TASKBAR_BOTTOM, + ), + ), ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 2aa300df4f7c..2aa300df4f7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt index 4768b88afe22..4768b88afe22 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt index f7cf4582566e..f7cf4582566e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java index 16ae4662c2d4..16ae4662c2d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt index 64796f1a757a..12bd5afcd94d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt @@ -17,8 +17,8 @@ package com.android.systemui.qs.external import android.app.StatusBarManager -import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId import com.android.internal.logging.UiEventLogger @@ -31,7 +31,7 @@ import org.junit.Test import org.junit.runner.RunWith @SmallTest -@RunWith(AndroidTestingRunner::class) +@RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class TileRequestDialogEventLoggerTest : SysuiTestCase() { @@ -138,4 +138,4 @@ class TileRequestDialogEventLoggerTest : SysuiTestCase() { assertThat(packageName).isEqualTo(PACKAGE_NAME) assertThat(this.instanceId).isEqualTo(instanceId) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt index 5bd3645b4cab..99d2da670172 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt @@ -30,9 +30,11 @@ import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileInput import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.screenrecord.RecordingController import com.android.systemui.settings.UserContextProvider +import com.android.systemui.settings.userFileManager import com.android.systemui.settings.userTracker import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.keyguardStateController @@ -81,10 +83,11 @@ class IssueRecordingUserActionInteractorTest : SysuiTestCase() { underTest = IssueRecordingUserActionInteractor( testDispatcher, + IssueRecordingState(userTracker, userFileManager), KeyguardDismissUtil( keyguardStateController, statusBarStateController, - activityStarter + activityStarter, ), keyguardStateController, dialogTransitionAnimator, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt index a1edfc1dbcd5..aceaab8206a2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt @@ -60,6 +60,7 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { private val iActivityManager = mock<IActivityManager>() private val notificationManager = mock<NotificationManager>() private val panelInteractor = mock<PanelInteractor>() + private val screenRecordingStartTimeStore = mock<ScreenRecordingStartTimeStore>() private lateinit var underTest: IssueRecordingServiceSession @@ -76,6 +77,7 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { iActivityManager, notificationManager, userContextProvider, + screenRecordingStartTimeStore, ) } @@ -90,7 +92,7 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { @Test fun stopsTracing_afterReceivingStopTracingCommand() { - underTest.stop(mContext.contentResolver) + underTest.stop() bgExecutor.runAllReady() Truth.assertThat(issueRecordingState.isRecording).isFalse() @@ -107,24 +109,24 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() { @Test fun requestBugreport_afterReceivingShareCommand_withTakeBugreportTrue() { - issueRecordingState.takeBugreport = true + underTest.takeBugReport = true val uri = mock<Uri>() underTest.share(0, uri) bgExecutor.runAllReady() - verify(iActivityManager).requestBugReportWithExtraAttachment(uri) + verify(iActivityManager).requestBugReportWithExtraAttachments(any()) } @Test fun sharesTracesDirectly_afterReceivingShareCommand_withTakeBugreportFalse() { - issueRecordingState.takeBugreport = false + underTest.takeBugReport = false val uri = mock<Uri>() underTest.share(0, uri) bgExecutor.runAllReady() - verify(traceurConnection).shareTraces(uri) + verify(traceurConnection).shareTraces(any()) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.kt new file mode 100644 index 000000000000..737b10166199 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStoreTest.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.recordissue + +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.settings.UserTracker +import com.android.systemui.settings.userTracker +import com.google.common.truth.Truth +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class ScreenRecordingStartTimeStoreTest : SysuiTestCase() { + private val userTracker: UserTracker = Kosmos().also { it.testCase = this }.userTracker + + private lateinit var underTest: ScreenRecordingStartTimeStore + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + underTest = ScreenRecordingStartTimeStore(userTracker) + } + + @Test + fun markStartTime_correctlyStoresValues_inSharedPreferences() { + underTest.markStartTime() + + val startTimeMetadata = underTest.userIdToScreenRecordingStartTime.get(userTracker.userId) + Truth.assertThat(startTimeMetadata).isNotNull() + Truth.assertThat(startTimeMetadata!!.getLong(ELAPSED_REAL_TIME_NANOS_KEY)).isNotNull() + Truth.assertThat(startTimeMetadata.getLong(REAL_TO_ELAPSED_TIME_OFFSET_NANOS_KEY)) + .isNotNull() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 0d5ddaeedb9e..bff3903e0114 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -52,6 +52,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.SysuiTestCase; import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.recordissue.ScreenRecordingStartTimeStore; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -95,6 +96,8 @@ public class RecordingServiceTest extends SysuiTestCase { private SysuiStatusBarStateController mStatusBarStateController; @Mock private ActivityStarter mActivityStarter; + @Mock + private ScreenRecordingStartTimeStore mScreenRecordingStartTimeStore; private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; @@ -108,9 +111,10 @@ public class RecordingServiceTest extends SysuiTestCase { RecordingController controller, Executor executor, Handler handler, UiEventLogger uiEventLogger, NotificationManager notificationManager, - UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { - super(controller, executor, handler, - uiEventLogger, notificationManager, userContextTracker, keyguardDismissUtil); + UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil, + ScreenRecordingStartTimeStore screenRecordingStartTimeStore) { + super(controller, executor, handler, uiEventLogger, notificationManager, + userContextTracker, keyguardDismissUtil, screenRecordingStartTimeStore); attachBaseContext(mContext); } } @@ -120,7 +124,7 @@ public class RecordingServiceTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mRecordingService = Mockito.spy(new RecordingServiceTestable(mController, mExecutor, mHandler, mUiEventLogger, mNotificationManager, - mUserContextTracker, mKeyguardDismissUtil)); + mUserContextTracker, mKeyguardDismissUtil, mScreenRecordingStartTimeStore)); // Return actual context info doReturn(mContext).when(mRecordingService).getApplicationContext(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt index ba6518f9bd2b..ba6518f9bd2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java index f8de714c6021..a831e6344a66 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java @@ -38,6 +38,8 @@ import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session; +import org.junit.Assume; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +51,12 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class ScrollCaptureControllerTest extends SysuiTestCase { + @Before + public void assumeOnDevice() { + // TODO(b/373930957) this class hangs under robolectric + Assume.assumeFalse(isRobolectricTest()); + } + private static final ScrollCaptureResponse EMPTY_RESPONSE = new ScrollCaptureResponse.Builder().build(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index d1d3b9b31a08..89ad6993cd13 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -85,7 +85,6 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.view.LongPressHandlingView; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; @@ -434,15 +433,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { new ShadeInteractorLegacyImpl( mTestScope.getBackgroundScope(), mFakeKeyguardRepository, - new SharedNotificationContainerInteractor( - new FakeConfigurationRepository(), - mContext, - () -> splitShadeStateController, - () -> mShadeInteractor, - mKeyguardInteractor, - deviceEntryUdfpsInteractor, - () -> mLargeScreenHeaderHelper - ), mShadeRepository ), mKosmos.getShadeModeInteractor()); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index adc336d18245..47eebf60f59f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.Notificatio import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController @@ -219,6 +220,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : primaryBouncerInteractor, alternateBouncerInteractor, mock(BouncerViewBinder::class.java), + mock(ConfigurationForwarder::class.java), ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index d61320e22e14..1c196c0eca4d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -15,8 +15,10 @@ */ package com.android.systemui.shade +import android.content.res.Configuration import android.os.SystemClock import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.widget.FrameLayout @@ -54,6 +56,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.window.StatusBarWindowStateController @@ -76,9 +79,11 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) @@ -101,7 +106,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var quickSettingsController: QuickSettingsController @Mock private lateinit var notificationStackScrollLayoutController: - NotificationStackScrollLayoutController + NotificationStackScrollLayoutController @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController @@ -117,12 +122,13 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock private lateinit var unfoldTransitionProgressProvider: - Optional<UnfoldTransitionProgressProvider> + Optional<UnfoldTransitionProgressProvider> @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor + @Mock lateinit var configurationForwarder: ConfigurationForwarder @Captor private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> @@ -136,10 +142,10 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) underTest = spy(NotificationShadeWindowView(context, null)) whenever( - underTest.findViewById<NotificationStackScrollLayout>( - R.id.notification_stack_scroller - ) + underTest.findViewById<NotificationStackScrollLayout>( + R.id.notification_stack_scroller ) + ) .thenReturn(notificationStackScrollLayout) whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container)) .thenReturn(mock()) @@ -191,6 +197,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { primaryBouncerInteractor, alternateBouncerInteractor, mock(), + configurationForwarder, ) controller.setupExpandedStatusBar() @@ -222,6 +229,23 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse() } + @Test + @DisableFlags(AConfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun onConfigurationChanged_configForwarderNotSet() { + underTest.onConfigurationChanged(Configuration()) + + verify(configurationForwarder, never()).onConfigurationChanged(any()) + } + + @Test + @EnableFlags(AConfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun onConfigurationChanged_configForwarderSet_propagatesConfig() { + val config = Configuration() + underTest.onConfigurationChanged(config) + + verify(configurationForwarder).onConfigurationChanged(eq(config)) + } + private fun captureInteractionEventHandler() { verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) interactionEventHandler = interactionEventHandlerCaptor.value diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt index f38bf13d0bda..ab5fa8ef43fb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt @@ -6,12 +6,11 @@ import android.graphics.Rect import android.view.DisplayCutout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.battery.BatteryMeterView -import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever +import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -19,6 +18,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -35,9 +36,12 @@ class QsBatteryModeControllerTest : SysuiTestCase() { const val QS_END_FRAME = 58 } + private val kosmos = testKosmos() + private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore + private val insetsProvider = insetsProviderStore.defaultDisplay + @JvmField @Rule val mockitoRule = MockitoJUnit.rule()!! - @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider @Mock private lateinit var mockedContext: Context @Mock private lateinit var mockedResources: Resources @@ -49,8 +53,7 @@ class QsBatteryModeControllerTest : SysuiTestCase() { whenever(mockedResources.getInteger(R.integer.fade_in_start_frame)).thenReturn(QS_END_FRAME) whenever(mockedResources.getInteger(R.integer.fade_out_complete_frame)) .thenReturn(QQS_START_FRAME) - - controller = QsBatteryModeController(mockedContext, insetsProvider) + controller = QsBatteryModeController(mockedContext, insetsProviderStore) } @Test @@ -96,5 +99,6 @@ class QsBatteryModeControllerTest : SysuiTestCase() { } private fun Int.prevFrameToFraction(): Float = (this - 1) / MOTION_LAYOUT_MAX_FRAME.toFloat() + private fun Int.nextFrameToFraction(): Float = (this + 1) / MOTION_LAYOUT_MAX_FRAME.toFloat() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java index a52f1737117a..2e759a363e20 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java @@ -36,16 +36,12 @@ import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; -import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.media.controls.domain.pipeline.MediaDataManager; @@ -55,7 +51,6 @@ import com.android.systemui.plugins.qs.QS; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragmentLegacy; import com.android.systemui.res.R; -import com.android.systemui.scene.domain.interactor.SceneInteractor; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.domain.interactor.ShadeInteractor; @@ -72,7 +67,6 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -179,27 +173,11 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { mStatusBarStateController = mKosmos.getStatusBarStateController(); mKosmos.getFakeDeviceProvisioningRepository().setDeviceProvisioned(true); - FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository(); PowerInteractor powerInteractor = mKosmos.getPowerInteractor(); - SceneInteractor sceneInteractor = mKosmos.getSceneInteractor(); - KeyguardTransitionInteractor keyguardTransitionInteractor = mKosmos.getKeyguardTransitionInteractor(); - KeyguardInteractor keyguardInteractor = new KeyguardInteractor( - mKeyguardRepository, - powerInteractor, - new FakeKeyguardBouncerRepository(), - new ConfigurationInteractor(configurationRepository), - mShadeRepository, - keyguardTransitionInteractor, - () -> sceneInteractor, - () -> mKosmos.getFromGoneTransitionInteractor(), - () -> mKosmos.getFromLockscreenTransitionInteractor(), - () -> mKosmos.getFromOccludedTransitionInteractor(), - () -> mKosmos.getSharedNotificationContainerInteractor(), - mTestScope); ResourcesSplitShadeStateController splitShadeStateController = new ResourcesSplitShadeStateController(); @@ -222,14 +200,6 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { new ShadeInteractorLegacyImpl( mTestScope.getBackgroundScope(), mKeyguardRepository, - new SharedNotificationContainerInteractor( - configurationRepository, - mContext, - () -> splitShadeStateController, - () -> mShadeInteractor, - keyguardInteractor, - deviceEntryUdfpsInteractor, - () -> mLargeScreenHeaderHelper), mShadeRepository ), mKosmos.getShadeModeInteractor()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java index 9a8df33a0276..9a8df33a0276 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt index 7d4918a30d9c..b5043ce700f1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -39,7 +39,7 @@ class ShadeRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { - underTest = ShadeRepositoryImpl(getContext()) + underTest = ShadeRepositoryImpl() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt index 4592b60e7c2c..238a1c1348b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt @@ -26,9 +26,9 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.kosmos.testScope -import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.data.repository.fakeShadeRepository +import com.android.systemui.shade.shadeTestUtil import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat @@ -46,6 +46,7 @@ import org.junit.runner.RunWith class ShadeInteractorLegacyImplTest : SysuiTestCase() { val kosmos = testKosmos() val testScope = kosmos.testScope + val shadeTestUtil = kosmos.shadeTestUtil val configurationRepository = kosmos.fakeConfigurationRepository val keyguardRepository = kosmos.fakeKeyguardRepository val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository @@ -87,7 +88,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { // WHEN split shade is enabled and QS is expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) - overrideResource(R.bool.config_use_split_notification_shade, true) + shadeTestUtil.setSplitShade(true) configurationRepository.onAnyConfigurationChange() shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(.7f) @@ -104,7 +105,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { // WHEN split shade is not enabled and QS is expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) - overrideResource(R.bool.config_use_split_notification_shade, false) + shadeTestUtil.setSplitShade(false) shadeRepository.setQsExpansion(.5f) shadeRepository.setLegacyShadeExpansion(1f) runCurrent() @@ -120,7 +121,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { // WHEN split shade is not enabled and QS is expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) - overrideResource(R.bool.config_use_split_notification_shade, false) + shadeTestUtil.setSplitShade(false) shadeRepository.setQsExpansion(1f) shadeRepository.setLegacyShadeExpansion(1f) runCurrent() @@ -136,7 +137,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { // WHEN split shade is not enabled and QS partly expanded keyguardRepository.setStatusBarState(StatusBarState.SHADE) - overrideResource(R.bool.config_use_split_notification_shade, false) + shadeTestUtil.setSplitShade(false) shadeRepository.setQsExpansion(.4f) shadeRepository.setLegacyShadeExpansion(1f) runCurrent() @@ -152,7 +153,7 @@ class ShadeInteractorLegacyImplTest : SysuiTestCase() { // WHEN split shade is not enabled and QS collapsed keyguardRepository.setStatusBarState(StatusBarState.SHADE) - overrideResource(R.bool.config_use_split_notification_shade, false) + shadeTestUtil.setSplitShade(false) shadeRepository.setQsExpansion(0f) shadeRepository.setLegacyShadeExpansion(.6f) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index bbe03f001b03..7e86ff3e95ac 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.plugins.clocks.ClockSettings +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -72,8 +73,7 @@ class DefaultClockProviderTest : SysuiTestCase() { .thenReturn(mockSmallClockView) whenever(layoutInflater.inflate(eq(R.layout.clock_default_large), any(), anyBoolean())) .thenReturn(mockLargeClockView) - whenever(resources.getString(R.string.clock_default_name)) - .thenReturn("DEFAULT_CLOCK_NAME") + whenever(resources.getString(R.string.clock_default_name)).thenReturn("DEFAULT_CLOCK_NAME") whenever(resources.getString(R.string.clock_default_description)) .thenReturn("DEFAULT_CLOCK_DESC") whenever(resources.getDrawable(R.drawable.clock_default_thumbnail, null)) @@ -108,7 +108,7 @@ class DefaultClockProviderTest : SysuiTestCase() { verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) - clock.initialize(resources, 0f, 0f) + clock.initialize(true, 0f, 0f) val expectedColor = 0 verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) @@ -167,21 +167,22 @@ class DefaultClockProviderTest : SysuiTestCase() { } @Test - fun defaultClock_events_onColorPaletteChanged() { + fun defaultClock_events_onThemeChanged_noSeed() { val expectedColor = 0 val clock = provider.createClock(DEFAULT_CLOCK_ID) verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) - clock.events.onColorPaletteChanged(resources) + clock.smallClock.events.onThemeChanged(ThemeConfig(true, null)) + clock.largeClock.events.onThemeChanged(ThemeConfig(true, null)) verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) } @Test - fun defaultClock_events_onSeedColorChanged() { + fun defaultClock_events_onThemeChanged_newSeed() { val initSeedColor = 10 val newSeedColor = 20 val clock = provider.createClock(ClockSettings(DEFAULT_CLOCK_ID, initSeedColor)) @@ -189,7 +190,8 @@ class DefaultClockProviderTest : SysuiTestCase() { verify(mockSmallClockView).setColors(DOZE_COLOR, initSeedColor) verify(mockLargeClockView).setColors(DOZE_COLOR, initSeedColor) - clock.events.onSeedColorChanged(newSeedColor) + clock.smallClock.events.onThemeChanged(ThemeConfig(true, newSeedColor)) + clock.largeClock.events.onThemeChanged(ThemeConfig(true, newSeedColor)) verify(mockSmallClockView).setColors(DOZE_COLOR, newSeedColor) verify(mockLargeClockView).setColors(DOZE_COLOR, newSeedColor) @@ -210,7 +212,7 @@ class DefaultClockProviderTest : SysuiTestCase() { fun test_aodClock_always_whiteColor() { val clock = provider.createClock(DEFAULT_CLOCK_ID) clock.smallClock.animations.doze(0.9f) // set AOD mode to active - clock.smallClock.events.onRegionDarknessChanged(true) + clock.smallClock.events.onThemeChanged(ThemeConfig(true, null)) verify((clock.smallClock.view as AnimatableClockView), never()).animateAppearOnLockscreen() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt index e2f22cde96de..e2f22cde96de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java index 58943ea3b4ee..c8ae3586221d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static android.app.Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -23,6 +25,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.app.Notification; +import android.app.NotificationChannelGroup; import android.app.RemoteInputHistoryItem; import android.net.Uri; import android.os.UserHandle; @@ -71,6 +74,25 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { } @Test + public void testRebuildWithRemoteInput_invalidData() { + Uri uri = mock(Uri.class); + String mimeType = "image/jpeg"; + String text = "image inserted"; + mEntry.getSbn().getNotification().extras.putParcelableArray( + EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + new NotificationChannelGroup[]{}); + StatusBarNotification newSbn = + mRebuilder.rebuildWithRemoteInputInserted( + mEntry, text, false, mimeType, uri); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + assertEquals(1, messages.length); + assertEquals(text, messages[0].getText()); + assertEquals(mimeType, messages[0].getMimeType()); + assertEquals(uri, messages[0].getUri()); + } + + @Test public void testRebuildWithRemoteInput_noExistingInput_image() { Uri uri = mock(Uri.class); String mimeType = "image/jpeg"; @@ -79,7 +101,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() - .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals(text, messages[0].getText()); assertEquals(mimeType, messages[0].getMimeType()); @@ -92,7 +114,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", false, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() - .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertFalse(newSbn.getNotification().extras @@ -107,7 +129,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() - .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertTrue(newSbn.getNotification().extras @@ -130,7 +152,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() - .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals("A Reply", messages[1].getText()); @@ -153,7 +175,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() - .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals(text, messages[1].getText()); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index eb5d9318c88f..2872900e01a5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -19,11 +19,12 @@ package com.android.systemui.statusbar.chips.notification.ui.viewmodel import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.ui.viewmodel.notifChipsViewModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.notification.data.model.activeNotificationModel import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore @@ -41,7 +42,7 @@ import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@EnableFlags(StatusBarNotifChips.FLAG_NAME) +@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) class NotifChipsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt index 69a76271f726..118dea6376bb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel import android.content.packageManager import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.commandline.CommandRegistry @@ -40,13 +40,13 @@ import org.mockito.kotlin.any import org.mockito.kotlin.whenever @SmallTest -class DemoNotifChipViewModelTest : SysuiTestCase() { +class DemoRonChipViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val pw = PrintWriter(StringWriter()) - private val underTest = kosmos.demoNotifChipViewModel + private val underTest = kosmos.demoRonChipViewModel @Before fun setUp() { @@ -56,61 +56,61 @@ class DemoNotifChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_flagOff_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - addDemoNotifChip() + addDemoRonChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_noPackage_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-notif")) + commandRegistry.onShellCommand(pw, arrayOf("demo-ron")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_hasPackage_shown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) } @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_hasText_shownWithText() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"), + arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test") ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java) } @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_supportsColor() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"), + arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343") ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) @@ -119,28 +119,28 @@ class DemoNotifChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) fun chip_hasHideArg_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) // First, show a chip - addDemoNotifChip() + addDemoRonChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) // Then, hide the chip - commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide")) + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } - private fun addDemoNotifChip() { - addDemoNotifChip(commandRegistry, pw) + private fun addDemoRonChip() { + Companion.addDemoRonChip(commandRegistry, pw) } companion object { - fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) { - commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) + fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) { + commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index e96def6d43a3..26ce7b956fde 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -25,6 +25,7 @@ import android.platform.test.annotations.DisableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue @@ -39,8 +40,7 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.phone.SystemUIDialog @@ -66,11 +66,13 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */ +/** + * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled. + */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@DisableFlags(StatusBarNotifChips.FLAG_NAME) +@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope @@ -97,11 +99,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoNotifChipViewModel.start() + kosmos.demoRonChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } @@ -323,7 +325,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { latest: OngoingActivityChipModel?, chipView: View, dialog: SystemUIDialog, - kosmos: Kosmos, + kosmos: Kosmos ): DialogInterface.OnClickListener { // Capture the action that would get invoked when the user clicks "Stop" on the dialog lateinit var dialogStopAction: DialogInterface.OnClickListener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt index b12d7c57e1fd..c5b857fc2b80 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt @@ -24,6 +24,7 @@ import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope @@ -36,10 +37,9 @@ import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -73,12 +73,14 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */ +/** + * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled. + */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@EnableFlags(StatusBarNotifChips.FLAG_NAME) -class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { +@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) +class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @@ -108,7 +110,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoNotifChipViewModel.start() + kosmos.demoRonChipViewModel.start() val icon = BitmapDrawable( context.resources, @@ -117,7 +119,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } - // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the + // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the // flow has the right behavior to verify that we don't break any existing functionality. @Test @@ -254,13 +256,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoNotifChip(commandRegistry, pw) + addDemoRonChip(commandRegistry, pw) val latest by collectLastValue(underTest.chips) assertIsScreenRecordChip(latest!!.primary) assertIsCallChip(latest!!.secondary) - // Demo notif chip is dropped + // Demo RON chip is dropped } @Test @@ -387,7 +389,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoNotifChip(commandRegistry, pw) + addDemoRonChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -395,7 +397,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.primaryChip) - assertIsDemoNotifChip(latest) + assertIsDemoRonChip(latest) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) @@ -429,7 +431,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoNotifChip(commandRegistry, pw) + addDemoRonChip(commandRegistry, pw) val latest by collectLastValue(underTest.primaryChip) @@ -451,15 +453,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { // WHEN the higher priority call is removed callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN the lower priority demo notif is used - assertIsDemoNotifChip(latest) + // THEN the lower priority demo RON is used + assertIsDemoRonChip(latest) } @Test fun chips_movesChipsAroundAccordingToPriority() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoNotifChip(commandRegistry, pw) + addDemoRonChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -467,16 +469,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) - assertIsDemoNotifChip(latest!!.primary) + assertIsDemoRonChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - // THEN the higher priority call chip is used as primary and demo notif is demoted to + // THEN the higher priority call chip is used as primary and demo ron is demoted to // secondary assertIsCallChip(latest!!.primary) - assertIsDemoNotifChip(latest!!.secondary) + assertIsDemoRonChip(latest!!.secondary) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -487,7 +489,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { ) // THEN the higher priority media projection chip is used as primary and call is demoted - // to secondary (and demo notif is dropped altogether) + // to secondary (and demo RON is dropped altogether) assertIsShareToAppChip(latest!!.primary) assertIsCallChip(latest!!.secondary) @@ -501,15 +503,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN media projection and demo notif remain + // THEN media projection and demo RON remain assertIsShareToAppChip(latest!!.primary) - assertIsDemoNotifChip(latest!!.secondary) + assertIsDemoRonChip(latest!!.secondary) // WHEN media projection is dropped mediaProjectionState.value = MediaProjectionState.NotProjecting - // THEN demo notif is promoted to primary - assertIsDemoNotifChip(latest!!.primary) + // THEN demo RON is promoted to primary + assertIsDemoRonChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -622,7 +624,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false)) } - private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) { + private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).icon) .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt index 72ffa0ee0855..72ffa0ee0855 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index b00f9e9f01d3..f502cabc21ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -61,6 +61,7 @@ import android.testing.TestableResources; import android.util.Log; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; import com.android.settingslib.R; import com.android.settingslib.graph.SignalDrawable; @@ -98,6 +99,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +@SmallTest public class NetworkControllerBaseTest extends SysuiTestCase { private static final String TAG = "NetworkControllerBaseTest"; protected static final int DEFAULT_LEVEL = 2; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java index 4241254e1fb9..4241254e1fb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java index 521cb4f4c42d..521cb4f4c42d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java index 7a579bacc86d..7a579bacc86d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index e312d00f3dc2..20a19a9b1399 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -98,9 +98,9 @@ class StatusBarInitializerTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - fun flagOff_doesNotInitializeViaCoreStartable() { + fun flagOff_startCalled_stillInitializes() { underTest.start() - assertThat(underTest.initialized).isFalse() + assertThat(underTest.initialized).isTrue() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt index ab8e878ab309..79230977b68e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt @@ -52,7 +52,7 @@ import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify -@EnableFlags(StatusBarSimpleFragment.FLAG_NAME) +@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) @SmallTest @RunWith(AndroidJUnit4::class) class StatusBarOrchestratorTest : SysuiTestCase() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt new file mode 100644 index 000000000000..0eebab0de831 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt @@ -0,0 +1,77 @@ +/* + * 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.statusbar.data.repository + +import android.view.Display.DEFAULT_DISPLAY +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.unconfinedTestDispatcher +import com.android.systemui.testKosmos +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { + + private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher } + private val testScope = kosmos.testScope + private val fakeDisplayRepository = kosmos.displayRepository + private val underTest = kosmos.multiDisplayStatusBarContentInsetsProviderStore + + @Before + fun start() { + underTest.start() + } + + @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) } + + @Test + fun forDisplay_startsInstances() = + testScope.runTest { + val instance = underTest.forDisplay(DEFAULT_DISPLAY) + + verify(instance).start() + } + + @Test + fun beforeDisplayRemoved_doesNotStopInstances() = + testScope.runTest { + val instance = underTest.forDisplay(DEFAULT_DISPLAY) + + verify(instance, never()).stop() + } + + @Test + fun displayRemoved_stopsInstance() = + testScope.runTest { + val instance = underTest.forDisplay(DEFAULT_DISPLAY) + + fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) + + verify(instance).stop() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java index d66b010daefd..d66b010daefd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt index 1f1680e0c273..75479ad35725 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt @@ -31,6 +31,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { private var isStarted = false + private var wasStarted = false private var scrimOffset = 0f private var contentHeight = 0f private var isCurrentGestureOverscroll = false @@ -46,7 +47,10 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT }, isCurrentGestureOverscroll = { isCurrentGestureOverscroll }, onStart = { isStarted = true }, - onStop = { isStarted = false }, + onStop = { + wasStarted = true + isStarted = false + }, ) @Test @@ -165,6 +169,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) + assertThat(wasStarted).isEqualTo(false) assertThat(isStarted).isEqualTo(false) } @@ -181,7 +186,9 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) - assertThat(isStarted).isEqualTo(true) + // Returning 0 offset will immediately stop the connection + assertThat(wasStarted).isEqualTo(true) + assertThat(isStarted).isEqualTo(false) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java index dc9c22f566bf..f1edb417a314 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE; import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer; import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -36,9 +37,11 @@ import static org.mockito.Mockito.when; import static java.util.Objects.requireNonNull; +import android.app.Flags; import android.database.ContentObserver; import android.os.Handler; import android.os.RemoteException; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import androidx.annotation.NonNull; @@ -61,6 +64,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.NotifInf import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider; import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -69,6 +73,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; +import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.util.settings.SecureSettings; @@ -82,10 +88,12 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; @SmallTest @RunWith(AndroidJUnit4.class) @@ -93,6 +101,7 @@ import java.util.Map; public class PreparationCoordinatorTest extends SysuiTestCase { private NotifCollectionListener mCollectionListener; private OnBeforeFinalizeFilterListener mBeforeFilterListener; + private OnBeforeTransformGroupsListener mBeforeTransformGroupsListener; private NotifFilter mUninflatedFilter; private NotifFilter mInflationErrorFilter; private NotifInflationErrorManager mErrorManager; @@ -101,6 +110,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase { @Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor; @Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor; + @Captor private ArgumentCaptor<OnBeforeTransformGroupsListener> + mBeforeTransformGroupsListenerCaptor; @Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor; @Mock private NotifSectioner mNotifSectioner; @@ -108,13 +119,14 @@ public class PreparationCoordinatorTest extends SysuiTestCase { @Mock private NotifPipeline mNotifPipeline; @Mock private IStatusBarService mService; @Mock private BindEventManagerImpl mBindEventManagerImpl; + @Mock private AppIconProvider mAppIconProvider; + @Mock private NotificationIconStyleProvider mNotificationIconStyleProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private SensitiveNotificationProtectionController mSensitiveNotifProtectionController; @Mock private Handler mHandler; @Mock private SecureSettings mSecureSettings; @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater(); - @Mock - HighPriorityProvider mHighPriorityProvider; + @Mock HighPriorityProvider mHighPriorityProvider; private SectionStyleProvider mSectionStyleProvider; @Mock private UserTracker mUserTracker; @Mock private GroupMembershipManager mGroupMembershipManager; @@ -126,6 +138,11 @@ public class PreparationCoordinatorTest extends SysuiTestCase { return new NotificationEntryBuilder().setSection(mNotifSection); } + @NonNull + private GroupEntryBuilder getGroupEntryBuilder() { + return new GroupEntryBuilder().setSection(mNotifSection); + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -138,7 +155,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mSectionStyleProvider, mUserTracker, mGroupMembershipManager - ); + ); mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build(); mInflationError = new Exception(TEST_MESSAGE); mErrorManager = new NotifInflationErrorManager(); @@ -153,6 +170,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mAdjustmentProvider, mService, mBindEventManagerImpl, + mAppIconProvider, + mNotificationIconStyleProvider, TEST_CHILD_BIND_CUTOFF, TEST_MAX_GROUP_DELAY); @@ -163,6 +182,15 @@ public class PreparationCoordinatorTest extends SysuiTestCase { mInflationErrorFilter = filters.get(0); mUninflatedFilter = filters.get(1); + if (android.app.Flags.notificationsRedesignAppIcons()) { + verify(mNotifPipeline).addOnBeforeTransformGroupsListener( + mBeforeTransformGroupsListenerCaptor.capture()); + mBeforeTransformGroupsListener = mBeforeTransformGroupsListenerCaptor.getValue(); + } else { + verify(mNotifPipeline, never()).addOnBeforeTransformGroupsListener( + mBeforeTransformGroupsListenerCaptor.capture()); + } + verify(mNotifPipeline).addCollectionListener(mCollectionListenerCaptor.capture()); mCollectionListener = mCollectionListenerCaptor.getValue(); @@ -199,6 +227,100 @@ public class PreparationCoordinatorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_NOTIFICATIONS_REDESIGN_APP_ICONS) + public void testPurgesAppIconProviderCache() { + // GIVEN a notification list + NotificationEntry entry1 = getNotificationEntryBuilder().setPkg("1").build(); + NotificationEntry entry2 = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry2bis = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry3 = getNotificationEntryBuilder().setPkg("3").build(); + + String groupKey1 = "group1"; + NotificationEntry summary = + getNotificationEntryBuilder() + .setPkg(groupKey1) + .setGroup(mContext, groupKey1) + .setGroupSummary(mContext, true) + .build(); + NotificationEntry child1 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + NotificationEntry child2 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + GroupEntry groupWithSummaryAndChildren = getGroupEntryBuilder().setKey(groupKey1) + .setSummary(summary).addChild(child1).addChild(child2).build(); + + String groupKey2 = "group2"; + NotificationEntry summary2 = + getNotificationEntryBuilder() + .setPkg(groupKey2) + .setGroup(mContext, groupKey2) + .setGroupSummary(mContext, true) + .build(); + GroupEntry summaryOnlyGroup = getGroupEntryBuilder().setKey(groupKey2) + .setSummary(summary2).build(); + + // WHEN onBeforeTransformGroup is called + mBeforeTransformGroupsListener.onBeforeTransformGroups( + List.of(entry1, entry2, entry2bis, entry3, + groupWithSummaryAndChildren, summaryOnlyGroup)); + + // THEN purge should be called + ArgumentCaptor<Collection<String>> argumentCaptor = ArgumentCaptor.forClass(List.class); + verify(mAppIconProvider).purgeCache(argumentCaptor.capture()); + List<String> actualList = argumentCaptor.getValue().stream().sorted().toList(); + List<String> expectedList = Stream.of("1", "2", "3", "group1", "group2") + .sorted().toList(); + assertEquals(expectedList, actualList); + } + + @Test + @EnableFlags(Flags.FLAG_NOTIFICATIONS_REDESIGN_APP_ICONS) + public void testPurgesNotificationIconStyleProviderCache() { + // GIVEN a notification list + NotificationEntry entry1 = getNotificationEntryBuilder().setPkg("1").build(); + NotificationEntry entry2 = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry2bis = getNotificationEntryBuilder().setPkg("2").build(); + NotificationEntry entry3 = getNotificationEntryBuilder().setPkg("3").build(); + + String groupKey1 = "group1"; + NotificationEntry summary = + getNotificationEntryBuilder() + .setPkg(groupKey1) + .setGroup(mContext, groupKey1) + .setGroupSummary(mContext, true) + .build(); + NotificationEntry child1 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + NotificationEntry child2 = getNotificationEntryBuilder().setGroup(mContext, groupKey1) + .setPkg(groupKey1).build(); + GroupEntry groupWithSummaryAndChildren = getGroupEntryBuilder().setKey(groupKey1) + .setSummary(summary).addChild(child1).addChild(child2).build(); + + String groupKey2 = "group2"; + NotificationEntry summary2 = + getNotificationEntryBuilder() + .setPkg(groupKey2) + .setGroup(mContext, groupKey2) + .setGroupSummary(mContext, true) + .build(); + GroupEntry summaryOnlyGroup = getGroupEntryBuilder().setKey(groupKey2) + .setSummary(summary2).build(); + + // WHEN onBeforeTransformGroup is called + mBeforeTransformGroupsListener.onBeforeTransformGroups( + List.of(entry1, entry2, entry2bis, entry3, + groupWithSummaryAndChildren, summaryOnlyGroup)); + + // THEN purge should be called + ArgumentCaptor<Collection<String>> argumentCaptor = ArgumentCaptor.forClass(List.class); + verify(mNotificationIconStyleProvider).purgeCache(argumentCaptor.capture()); + List<String> actualList = argumentCaptor.getValue().stream().sorted().toList(); + List<String> expectedList = Stream.of("1", "2", "3", "group1", "group2") + .sorted().toList(); + assertEquals(expectedList, actualList); + } + + @Test public void testInflatesNewNotification() { // WHEN there is a new notification mCollectionListener.onEntryInit(mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index a21ca9458b00..a21ca9458b00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index b278f1a48b3d..b278f1a48b3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 6425da46fc67..6425da46fc67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index add7ac95e8c4..33bf3507adb0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -161,7 +161,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S fun validateMarginStartInSplitShade() = testScope.runTest { shadeTestUtil.setSplitShade(true) - overrideResource(R.dimen.notification_panel_margin_horizontal, 20) + overrideDimensionPixelSize(R.dimen.notification_panel_margin_horizontal, 20) val dimens by collectLastValue(underTest.configurationBasedDimensions) @@ -174,7 +174,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S fun validateMarginStart() = testScope.runTest { shadeTestUtil.setSplitShade(false) - overrideResource(R.dimen.notification_panel_margin_horizontal, 20) + overrideDimensionPixelSize(R.dimen.notification_panel_margin_horizontal, 20) val dimens by collectLastValue(underTest.configurationBasedDimensions) @@ -189,8 +189,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5) shadeTestUtil.setSplitShade(true) overrideResource(R.bool.config_use_large_screen_shade_header, true) - overrideResource(R.dimen.large_screen_shade_header_height, 10) - overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10) + overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50) val paddingTop by collectLastValue(underTest.paddingTopDimen) configurationRepository.onAnyConfigurationChange() @@ -205,8 +205,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10) shadeTestUtil.setSplitShade(false) overrideResource(R.bool.config_use_large_screen_shade_header, true) - overrideResource(R.dimen.large_screen_shade_header_height, 10) - overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10) + overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50) val paddingTop by collectLastValue(underTest.paddingTopDimen) @@ -221,8 +221,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(10) shadeTestUtil.setSplitShade(false) overrideResource(R.bool.config_use_large_screen_shade_header, false) - overrideResource(R.dimen.large_screen_shade_header_height, 10) - overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10) + overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50) val paddingTop by collectLastValue(underTest.paddingTopDimen) @@ -263,8 +263,11 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()) .thenReturn(headerHelperHeight) overrideResource(R.bool.config_use_large_screen_shade_header, true) - overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight) - overrideResource(R.dimen.notification_panel_margin_top, 0) + overrideDimensionPixelSize( + R.dimen.large_screen_shade_header_height, + headerResourceHeight, + ) + overrideDimensionPixelSize(R.dimen.notification_panel_margin_top, 0) val dimens by collectLastValue(underTest.configurationBasedDimensions) @@ -282,8 +285,11 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()) .thenReturn(headerHelperHeight) overrideResource(R.bool.config_use_large_screen_shade_header, true) - overrideResource(R.dimen.large_screen_shade_header_height, headerResourceHeight) - overrideResource(R.dimen.notification_panel_margin_top, 0) + overrideDimensionPixelSize( + R.dimen.large_screen_shade_header_height, + headerResourceHeight, + ) + overrideDimensionPixelSize(R.dimen.notification_panel_margin_top, 0) val dimens by collectLastValue(underTest.configurationBasedDimensions) @@ -480,8 +486,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S fun validateMarginTop() = testScope.runTest { overrideResource(R.bool.config_use_large_screen_shade_header, false) - overrideResource(R.dimen.large_screen_shade_header_height, 50) - overrideResource(R.dimen.notification_panel_margin_top, 0) + overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 50) + overrideDimensionPixelSize(R.dimen.notification_panel_margin_top, 0) val dimens by collectLastValue(underTest.configurationBasedDimensions) @@ -631,6 +637,45 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S @Test @DisableSceneContainer + fun boundsStableWhenGoingToAlternateBouncer() = + testScope.runTest { + val bounds by collectLastValue(underTest.bounds) + + // Start on lockscreen + showLockscreen() + + keyguardInteractor.setNotificationContainerBounds( + NotificationContainerBounds(top = 1f, bottom = 2f) + ) + + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f)) + + // Begin transition to AOD + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.STARTED) + ) + runCurrent() + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.RUNNING) + ) + runCurrent() + + // This is the last step before FINISHED is sent, which could trigger a change in bounds + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.RUNNING) + ) + runCurrent() + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f)) + + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.FINISHED) + ) + runCurrent() + assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f)) + } + + @Test + @DisableSceneContainer fun boundsDoNotChangeWhileLockscreenToAodTransitionIsActive() = testScope.runTest { val bounds by collectLastValue(underTest.bounds) @@ -676,8 +721,8 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5) shadeTestUtil.setSplitShade(true) overrideResource(R.bool.config_use_large_screen_shade_header, true) - overrideResource(R.dimen.large_screen_shade_header_height, 10) - overrideResource(R.dimen.keyguard_split_shade_top_margin, 50) + overrideDimensionPixelSize(R.dimen.large_screen_shade_header_height, 10) + overrideDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin, 50) configurationRepository.onAnyConfigurationChange() runCurrent() @@ -1223,6 +1268,75 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S assertThat(alpha).isEqualTo(1f) } + @Test + @DisableSceneContainer + fun notificationAbsoluteBottom() = + testScope.runTest { + var notificationCount = 2 + val calculateSpace = { _: Float, _: Boolean -> notificationCount } + val shelfHeight = 10F + val heightForNotification = 20F + val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight } + val stackAbsoluteBottom by + collectLastValue( + underTest.getNotificationStackAbsoluteBottom( + calculateSpace, + calculateHeight, + shelfHeight, + ) + ) + advanceTimeBy(50L) + showLockscreen() + + shadeTestUtil.setSplitShade(false) + keyguardInteractor.setNotificationContainerBounds( + NotificationContainerBounds(top = 100F, bottom = 300F) + ) + configurationRepository.onAnyConfigurationChange() + + assertThat(stackAbsoluteBottom).isEqualTo(150F) + + // Also updates when directly requested (as it would from NotificationStackScrollLayout) + notificationCount = 3 + sharedNotificationContainerInteractor.notificationStackChanged() + advanceTimeBy(50L) + assertThat(stackAbsoluteBottom).isEqualTo(170F) + } + + @Test + @DisableSceneContainer + fun notificationAbsoluteBottom_maxNotificationIsZero_noShelfHeight() = + testScope.runTest { + var notificationCount = 2 + val calculateSpace = { _: Float, _: Boolean -> notificationCount } + val shelfHeight = 10F + val heightForNotification = 20F + val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight } + val stackAbsoluteBottom by + collectLastValue( + underTest.getNotificationStackAbsoluteBottom( + calculateSpace, + calculateHeight, + shelfHeight, + ) + ) + advanceTimeBy(50L) + showLockscreen() + + shadeTestUtil.setSplitShade(false) + keyguardInteractor.setNotificationContainerBounds( + NotificationContainerBounds(top = 100F, bottom = 300F) + ) + configurationRepository.onAnyConfigurationChange() + + assertThat(stackAbsoluteBottom).isEqualTo(150F) + + notificationCount = 0 + sharedNotificationContainerInteractor.notificationStackChanged() + advanceTimeBy(50L) + assertThat(stackAbsoluteBottom).isEqualTo(100F) + } + private suspend fun TestScope.showLockscreen() { shadeTestUtil.setQsExpansion(0f) shadeTestUtil.setLockscreenShadeExpansion(0f) @@ -1310,4 +1424,9 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S communalSceneRepository.setTransitionState(transitionState) runCurrent() } + + private fun overrideDimensionPixelSize(id: Int, pixelSize: Int) { + overrideResource(id, pixelSize) + configurationRepository.setDimensionPixelSize(id, pixelSize) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 68e17c1b2d73..157f8189276a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -63,6 +63,7 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; import com.android.systemui.statusbar.phone.ui.TintedIconManager; @@ -119,6 +120,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { @Mock private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider; @Mock + private StatusBarContentInsetsProviderStore mStatusBarContentInsetsProviderStore; + @Mock private UserManager mUserManager; @Mock private StatusBarUserChipViewModel mStatusBarUserChipViewModel; @@ -143,7 +146,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mShadeViewStateProvider = new TestShadeViewStateProvider(); MockitoAnnotations.initMocks(this); - + when(mStatusBarContentInsetsProviderStore.getDefaultDisplay()) + .thenReturn(mStatusBarContentInsetsProvider); when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager); allowTestableLooperAsMainThread(); @@ -175,7 +179,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mKosmos.getKeyguardStatusBarViewModel(), mBiometricUnlockController, mStatusBarStateController, - mStatusBarContentInsetsProvider, + mStatusBarContentInsetsProviderStore, mUserManager, mStatusBarUserChipViewModel, mSecureSettings, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt index ba5fb7f8df00..a862fdfeca22 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -36,14 +36,15 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import junit.framework.Assert.assertTrue +import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @SmallTest @@ -1018,7 +1019,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { // Regression test for b/245799099 @Test - fun onMaxBoundsChanged_listenerNotified() { + fun onMaxBoundsChanged_afterStart_listenerNotified() { // Start out with an existing configuration with bounds configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100) configurationController.onConfigurationChanged(configuration) @@ -1038,6 +1039,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { triggered = true } } + provider.start() provider.addCallback(listener) // WHEN the config is updated with new bounds @@ -1049,7 +1051,39 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test - fun onDensityOrFontScaleChanged_listenerNotified() { + fun onMaxBoundsChanged_beforeStart_listenerNotNotified() { + // Start out with an existing configuration with bounds + configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100) + configurationController.onConfigurationChanged(configuration) + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.addCallback(listener) + + // WHEN the config is updated with new bounds + // but provider is not started + configuration.windowConfiguration.setMaxBounds(0, 0, 456, 789) + configurationController.onConfigurationChanged(configuration) + + // THEN the listener is not notified + assertThat(listener.triggered).isFalse() + } + + @Test + fun onDensityOrFontScaleChanged_beforeStart_listenerNotNotified() { configuration.densityDpi = 12 val provider = StatusBarContentInsetsProviderImpl( @@ -1069,6 +1103,36 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } provider.addCallback(listener) + // WHEN the config is updated, but the provider is not started + configuration.densityDpi = 20 + configurationController.onConfigurationChanged(configuration) + + // THEN the listener is NOT notified + assertThat(listener.triggered).isFalse() + } + + @Test + fun onDensityOrFontScaleChanged_afterStart_listenerNotified() { + configuration.densityDpi = 12 + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.start() + provider.addCallback(listener) + // WHEN the config is updated configuration.densityDpi = 20 configurationController.onConfigurationChanged(configuration) @@ -1078,7 +1142,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test - fun onThemeChanged_listenerNotified() { + fun onThemeChanged_afterStart_listenerNotified() { val provider = StatusBarContentInsetsProviderImpl( contextMock, @@ -1095,6 +1159,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { triggered = true } } + provider.start() provider.addCallback(listener) configurationController.notifyThemeChanged() @@ -1103,18 +1168,44 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertThat(listener.triggered).isTrue() } + @Test + fun onThemeChanged_beforeStart_listenerNotNotified() { + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.addCallback(listener) + + configurationController.notifyThemeChanged() + + assertThat(listener.triggered).isFalse() + } + private fun assertRects( expected: Rect, actual: Rect, @Rotation currentRotation: Int, @Rotation targetRotation: Int, ) { - assertTrue( - "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + - " targetRotation=${RotationUtils.toString(targetRotation)}" + - " expected=$expected actual=$actual", - expected.equals(actual), - ) + assertWithMessage( + "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + + " targetRotation=${RotationUtils.toString(targetRotation)}" + + " expected=$expected actual=$actual" + ) + .that(actual) + .isEqualTo(expected) } private fun setNoCutout() { @@ -1136,8 +1227,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } private fun setCameraProtectionBounds(protectionBounds: Rect) { - val protectionInfo = - mock<CameraProtectionInfo> { whenever(this.bounds).thenReturn(protectionBounds) } + val protectionInfo = mock<CameraProtectionInfo> { on { bounds } doReturn protectionBounds } whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 94753f7e5203..21a317aab36a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,6 +57,7 @@ import android.window.WindowOnBackInvokedDispatcher; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; @@ -675,8 +677,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { backCallback.onBackProgressed(event); verify(mBouncerViewDelegateBackCallback).onBackProgressed(eq(event)); - backCallback.onBackInvoked(); - verify(mBouncerViewDelegateBackCallback).onBackInvoked(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(backCallback::onBackInvoked); + verify(mBouncerViewDelegateBackCallback, timeout(1000)).onBackInvoked(); backCallback.onBackCancelled(); verify(mBouncerViewDelegateBackCallback).onBackCancelled(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java index 3f33d2f89f5e..3f33d2f89f5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt index 29e9ba752b36..d9d81692e2b6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt @@ -20,6 +20,8 @@ import android.view.MotionEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.LEFT +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.RIGHT import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted @@ -61,23 +63,46 @@ class BackGestureRecognizerTest : SysuiTestCase() { } @Test - fun triggersProgressRelativeToDistance() { - assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) - assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f) - assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f) - assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f) + fun triggersProgressRelativeToDistanceWhenSwipingLeft() { + assertProgressWhileMovingFingers( + deltaX = -SWIPE_DISTANCE / 2, + expected = InProgress(progress = 0.5f, direction = LEFT), + ) + assertProgressWhileMovingFingers( + deltaX = -SWIPE_DISTANCE, + expected = InProgress(progress = 1f, direction = LEFT), + ) } - private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) { + @Test + fun triggersProgressRelativeToDistanceWhenSwipingRight() { + assertProgressWhileMovingFingers( + deltaX = SWIPE_DISTANCE / 2, + expected = InProgress(progress = 0.5f, direction = RIGHT), + ) + assertProgressWhileMovingFingers( + deltaX = SWIPE_DISTANCE, + expected = InProgress(progress = 1f, direction = RIGHT), + ) + } + + private fun assertProgressWhileMovingFingers(deltaX: Float, expected: InProgress) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, - expectedState = InProgress(progress = expectedProgress), + expectedState = expected, ) } @Test fun triggeredProgressIsNoBiggerThanOne() { - assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f) + assertProgressWhileMovingFingers( + deltaX = SWIPE_DISTANCE * 2, + expected = InProgress(progress = 1f, direction = RIGHT), + ) + assertProgressWhileMovingFingers( + deltaX = -SWIPE_DISTANCE * 2, + expected = InProgress(progress = 1f, direction = LEFT), + ) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt new file mode 100644 index 000000000000..7d5559933cd8 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorTest.kt @@ -0,0 +1,99 @@ +/* + * 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.volume.dialog.ringer.domain + +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.media.AudioManager.STREAM_RING +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.fakeVolumeDialogController +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class VolumeDialogRingerInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val controller = kosmos.fakeVolumeDialogController + + private lateinit var underTest: VolumeDialogRingerInteractor + + @Before + fun setUp() { + underTest = kosmos.volumeDialogRingerInteractor + controller.setStreamVolume(STREAM_RING, 50) + } + + @Test + fun setRingerMode_normal() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_NORMAL)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_NORMAL)) + } + + @Test + fun setRingerMode_silent() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_SILENT)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_SILENT)) + } + + @Test + fun setRingerMode_vibrate() = + testScope.runTest { + runCurrent() + val ringerModel by collectLastValue(underTest.ringerModel) + + underTest.setRingerMode(RingerMode(RINGER_MODE_VIBRATE)) + controller.getState() + runCurrent() + + assertThat(ringerModel).isNotNull() + assertThat(ringerModel?.currentRingerMode).isEqualTo(RingerMode(RINGER_MODE_VIBRATE)) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.kt new file mode 100644 index 000000000000..faf01eddbcc3 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelTest.kt @@ -0,0 +1,144 @@ +/* + * 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.volume.dialog.ringer.ui.viewmodel + +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.media.AudioManager.STREAM_RING +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.haptics.fakeVibratorHelper +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.fakeVolumeDialogController +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +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 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class VolumeDialogRingerDrawerViewModelTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val controller = kosmos.fakeVolumeDialogController + private val vibratorHelper = kosmos.fakeVibratorHelper + + private lateinit var underTest: VolumeDialogRingerDrawerViewModel + + @Before + fun setUp() { + underTest = kosmos.volumeDialogRingerDrawerViewModel + } + + @Test + fun onSelectedRingerNormalModeButtonClicked_openDrawer() = + testScope.runTest { + val ringerViewModel by collectLastValue(underTest.ringerViewModel) + val normalRingerMode = RingerMode(RINGER_MODE_NORMAL) + + setUpRingerModeAndOpenDrawer(normalRingerMode) + + assertThat(ringerViewModel).isNotNull() + assertThat(ringerViewModel?.drawerState) + .isEqualTo(RingerDrawerState.Open(normalRingerMode)) + } + + @Test + fun onSelectedRingerButtonClicked_drawerOpened_closeDrawer() = + testScope.runTest { + val ringerViewModel by collectLastValue(underTest.ringerViewModel) + val normalRingerMode = RingerMode(RINGER_MODE_NORMAL) + + setUpRingerModeAndOpenDrawer(normalRingerMode) + underTest.onRingerButtonClicked(normalRingerMode) + controller.getState() + + assertThat(ringerViewModel).isNotNull() + assertThat(ringerViewModel?.drawerState) + .isEqualTo(RingerDrawerState.Closed(normalRingerMode)) + } + + @Test + fun onNewRingerButtonClicked_drawerOpened_updateRingerMode_closeDrawer() = + testScope.runTest { + val ringerViewModel by collectLastValue(underTest.ringerViewModel) + val vibrateRingerMode = RingerMode(RINGER_MODE_VIBRATE) + + setUpRingerModeAndOpenDrawer(RingerMode(RINGER_MODE_NORMAL)) + // Select vibrate ringer mode. + underTest.onRingerButtonClicked(vibrateRingerMode) + controller.getState() + runCurrent() + + assertThat(ringerViewModel).isNotNull() + assertThat( + ringerViewModel + ?.availableButtons + ?.get(ringerViewModel!!.currentButtonIndex) + ?.ringerMode + ) + .isEqualTo(vibrateRingerMode) + assertThat(ringerViewModel?.drawerState) + .isEqualTo(RingerDrawerState.Closed(vibrateRingerMode)) + + val silentRingerMode = RingerMode(RINGER_MODE_SILENT) + // Open drawer + underTest.onRingerButtonClicked(vibrateRingerMode) + controller.getState() + + // Select silent ringer mode. + underTest.onRingerButtonClicked(silentRingerMode) + controller.getState() + runCurrent() + + assertThat(ringerViewModel).isNotNull() + assertThat( + ringerViewModel + ?.availableButtons + ?.get(ringerViewModel!!.currentButtonIndex) + ?.ringerMode + ) + .isEqualTo(silentRingerMode) + assertThat(ringerViewModel?.drawerState) + .isEqualTo(RingerDrawerState.Closed(silentRingerMode)) + assertThat(controller.hasScheduledTouchFeedback).isFalse() + assertThat(vibratorHelper.totalVibrations).isEqualTo(2) + } + + private fun TestScope.setUpRingerModeAndOpenDrawer(selectedRingerMode: RingerMode) { + controller.setStreamVolume(STREAM_RING, 50) + controller.setRingerMode(selectedRingerMode.value, false) + runCurrent() + + underTest.onRingerButtonClicked(RingerMode(selectedRingerMode.value)) + controller.getState() + runCurrent() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt index 7ad904e9b436..bfafdab003aa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt @@ -14,16 +14,18 @@ * limitations under the License. */ -package com.android.systemui.qs.panels.domain.interactor +package com.android.systemui.volume.dialog.sliders.domain.interactor -import android.content.pm.UserInfo +import android.testing.TestableLooper 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.kosmos.testScope +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.plugins.fakeVolumeDialogController import com.android.systemui.testKosmos -import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent @@ -35,56 +37,54 @@ import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -class IconLabelVisibilityInteractorTest : SysuiTestCase() { +@TestableLooper.RunWithLooper +class VolumeDialogSliderInteractorTest : SysuiTestCase() { + private val kosmos = testKosmos() - private val underTest = with(kosmos) { iconLabelVisibilityInteractor } + + private lateinit var underTest: VolumeDialogSliderInteractor @Before fun setUp() { - with(kosmos) { fakeUserRepository.setUserInfos(USERS) } + underTest = kosmos.volumeDialogSliderInteractor } @Test - fun changingShowLabels_receivesCorrectShowLabels() = + fun settingStreamVolume_setsActiveStream() = with(kosmos) { testScope.runTest { - val showLabels by collectLastValue(underTest.showLabels) - - underTest.setShowLabels(false) runCurrent() - assertThat(showLabels).isFalse() + // initialize the stream model + fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0) - underTest.setShowLabels(true) + val sliderModel by collectLastValue(underTest.slider) + underTest.setStreamVolume(1) runCurrent() - assertThat(showLabels).isTrue() + + assertThat(sliderModel!!.isActive).isTrue() } } @Test - fun changingUser_receivesCorrectShowLabels() = + fun streamVolumeIs_minMaxAreEnforced() = with(kosmos) { testScope.runTest { - val showLabels by collectLastValue(underTest.showLabels) - - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - underTest.setShowLabels(false) runCurrent() - assertThat(showLabels).isFalse() + fakeVolumeDialogController.updateState { + states.put( + volumeDialogSliderType.audioStream, + VolumeDialogController.StreamState().apply { + levelMin = 0 + level = 2 + levelMax = 1 + }, + ) + } - fakeUserRepository.setSelectedUserInfo(ANOTHER_USER) - underTest.setShowLabels(true) + val sliderModel by collectLastValue(underTest.slider) runCurrent() - assertThat(showLabels).isTrue() - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - runCurrent() - assertThat(showLabels).isFalse() + assertThat(sliderModel!!.level).isEqualTo(1) } } - - companion object { - private val PRIMARY_USER = UserInfo(0, "user 0", UserInfo.FLAG_MAIN) - private val ANOTHER_USER = UserInfo(1, "user 1", UserInfo.FLAG_FULL) - private val USERS = listOf(PRIMARY_USER, ANOTHER_USER) - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt index 59676ce126da..111c232280c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt @@ -25,9 +25,4 @@ class FakeWallpaperRepository : WallpaperRepository { override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null) override val wallpaperSupportsAmbientMode = MutableStateFlow(false) override var rootView: View? = null - private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) - - override fun setNotificationStackAbsoluteBottom(bottom: Float) { - _notificationStackAbsoluteBottom.value = bottom - } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index 6d27b6f9637b..f975c4f13ff4 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -13,7 +13,6 @@ */ package com.android.systemui.plugins.clocks -import android.content.res.Resources import android.graphics.Rect import android.graphics.drawable.Drawable import android.view.View @@ -85,7 +84,7 @@ interface ClockController { val events: ClockEvents /** Initializes various rendering parameters. If never called, provides reasonable defaults. */ - fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) + fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) /** Optional method for dumping debug information */ fun dump(pw: PrintWriter) @@ -108,6 +107,10 @@ interface ClockFaceController { val config: ClockFaceConfig @get:SimpleProperty + /** Current theme information the clock is using */ + val theme: ThemeConfig + + @get:SimpleProperty /** Events specific to this clock face */ val events: ClockFaceEvents @@ -190,12 +193,6 @@ interface ClockEvents { /** Call whenever the locale changes */ fun onLocaleChanged(locale: Locale) - /** Call whenever the color palette should update */ - fun onColorPaletteChanged(resources: Resources) - - /** Call if the seed color has changed and should be updated */ - fun onSeedColorChanged(seedColor: Int?) - /** Call whenever the weather data should update */ fun onWeatherDataChanged(data: WeatherData) @@ -269,11 +266,13 @@ interface ClockFaceEvents { fun onTimeTick() /** - * Region Darkness specific to the clock face. - * - isRegionDark = dark theme -> clock should be light - * - !isRegionDark = light theme -> clock should be dark + * Call whenever the theme or seedColor is updated + * + * Theme can be specific to the clock face. + * - isDarkTheme -> clock should be light + * - !isDarkTheme -> clock should be dark */ - fun onRegionDarknessChanged(isRegionDark: Boolean) + fun onThemeChanged(theme: ThemeConfig) /** * Call whenever font settings change. Pass in a target font size in pixels. The specific clock @@ -294,6 +293,8 @@ interface ClockFaceEvents { fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) } +data class ThemeConfig(val isDarkTheme: Boolean, val seedColor: Int?) + /** Tick rates for clocks */ enum class ClockTickRate(val value: Int) { PER_MINUTE(2), // Update the clock once per minute. diff --git a/packages/SystemUI/plugin_core/AndroidManifest.xml b/packages/SystemUI/plugin_core/AndroidManifest.xml deleted file mode 100644 index df835fd8e32d..000000000000 --- a/packages/SystemUI/plugin_core/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.plugin_core"> - - <uses-sdk - android:minSdkVersion="28" /> - -</manifest> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 917e13182f85..5aff12464534 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -62,9 +62,9 @@ <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"دوباره امتحان کنید یا پین را وارد کنید"</string> <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"دوباره امتحان کنید یا گذرواژه را وارد کنید"</string> <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"دوباره امتحان کنید یا الگو را رسم کنید"</string> - <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"بعداز دفعات زیادی تلاش ناموفق، پین لازم است"</string> - <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"بعداز دفعات زیادی تلاش ناموفق، گذرواژه لازم است"</string> - <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"بعداز دفعات زیادی تلاش ناموفق، الگو لازم است"</string> + <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"پساز تلاشهای بسیار زیاد ناموفق، پین لازم است"</string> + <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"پساز تلاشهای بسیار زیاد ناموفق، گذرواژه لازم است"</string> + <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"پساز تلاشهای بسیار زیاد ناموفق، الگو لازم است"</string> <string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"قفلگشایی با پین یا اثر انگشت"</string> <string name="kg_unlock_with_password_or_fp" msgid="2251295907826814237">"قفلگشایی با گذرواژه یا اثر انگشت"</string> <string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"قفلگشایی با الگو یا اثر انگشت"</string> @@ -77,12 +77,12 @@ <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"امنیت بیشتر لازم است. گذرواژه مدتی استفاده نشده است."</string> <string name="kg_prompt_pattern_auth_timeout" msgid="1860605401869262178">"امنیت بیشتر لازم است. مدتی از الگو استفاده نشده است."</string> <string name="kg_prompt_auth_timeout" msgid="6620679830980315048">"امنیت بیشتر لازم است. قفل دستگاه مدتی باز نشده است."</string> - <string name="kg_face_locked_out" msgid="2751559491287575">"قفلگشایی با چهره ممکن نیست. دفعات زیادی تلاش ناموفق"</string> - <string name="kg_fp_locked_out" msgid="6228277682396768830">"قفل با اثرانگشت باز نمیشود. دفعات زیادی تلاش ناموفق"</string> + <string name="kg_face_locked_out" msgid="2751559491287575">"قفلگشایی با چهره ممکن نیست. تلاشهای بسیار زیاد ناموفق."</string> + <string name="kg_fp_locked_out" msgid="6228277682396768830">"قفل با اثرانگشت باز نمیشود. تلاشهای بسیار زیاد ناموفق."</string> <string name="kg_trust_agent_disabled" msgid="5400691179958727891">"عامل معتبر دردسترس نیست"</string> <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"تلاشهای بسیار زیادی با پین اشتباه انجام شده است"</string> - <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"تلاشهای بسیار زیادی با الگوی اشتباه انجام شده است"</string> - <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"تلاشهای بسیار زیادی با گذرواژه اشتباه انجام شده است"</string> + <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"تلاشهای بسیار زیاد با الگوی اشتباه"</string> + <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"تلاشهای بسیار زیاد با گذرواژه اشتباه"</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# ثانیه دیگر دوباره امتحان کنید.}one{# ثانیه دیگر دوباره امتحان کنید.}other{# ثانیه دیگر دوباره امتحان کنید.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"پین سیمکارت را وارد کنید."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"پین سیمکارت «<xliff:g id="CARRIER">%1$s</xliff:g>» را وارد کنید."</string> diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_history.xml b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml new file mode 100644 index 000000000000..0460a7268fcc --- /dev/null +++ b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="20dp" + android:height="20dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml new file mode 100644 index 000000000000..800060db7757 --- /dev/null +++ b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="20dp" + android:height="20dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml index 7d7498feeba6..98e75058fd3e 100644 --- a/packages/SystemUI/res/drawable/volume_dialog_background.xml +++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml @@ -18,5 +18,5 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> <corners android:radius="@dimen/volume_dialog_background_corner_radius" /> - <solid android:color="?androidprv:attr/materialColorSurface" /> + <solid android:color="?androidprv:attr/colorSurface" /> </shape> diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml deleted file mode 100644 index f77db956a493..000000000000 --- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml +++ /dev/null @@ -1,67 +0,0 @@ -<!-- - 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. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/volume_dialog_container" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:orientation="horizontal" - android:showDividers="middle|end|beginning" - android:theme="@style/volume_dialog_theme"> - - <LinearLayout - android:id="@+id/volume_dialog_floating_sliders_container" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:background="@drawable/volume_dialog_background" - android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:gravity="bottom" - android:orientation="horizontal" - android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding" - android:showDividers="middle" /> - - <LinearLayout - android:layout_width="@dimen/volume_dialog_width" - android:layout_height="wrap_content" - android:background="@drawable/volume_dialog_background" - android:divider="@drawable/volume_dialog_spacer" - android:gravity="center_horizontal" - android:orientation="vertical" - android:paddingVertical="@dimen/volume_dialog_vertical_padding" - android:showDividers="middle"> - - <FrameLayout - android:id="@+id/volume_dialog_ringer_button" - android:layout_width="@dimen/volume_dialog_button_size" - android:layout_height="@dimen/volume_dialog_button_size" /> - - <include - android:id="@+id/volume_dialog_slider" - layout="@layout/volume_dialog_slider" /> - - <Button - android:id="@+id/volume_dialog_settings" - android:layout_width="@dimen/volume_dialog_button_size" - android:layout_height="@dimen/volume_dialog_button_size" - android:background="@drawable/ripple_drawable_20dp" - android:contentDescription="@string/accessibility_volume_settings" - android:soundEffectsEnabled="false" - android:src="@drawable/horizontal_ellipsis" - android:tint="?androidprv:attr/materialColorPrimary" /> - </LinearLayout> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml deleted file mode 100644 index f77db956a493..000000000000 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ /dev/null @@ -1,67 +0,0 @@ -<!-- - 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. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/volume_dialog_container" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:orientation="horizontal" - android:showDividers="middle|end|beginning" - android:theme="@style/volume_dialog_theme"> - - <LinearLayout - android:id="@+id/volume_dialog_floating_sliders_container" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:background="@drawable/volume_dialog_background" - android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:gravity="bottom" - android:orientation="horizontal" - android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding" - android:showDividers="middle" /> - - <LinearLayout - android:layout_width="@dimen/volume_dialog_width" - android:layout_height="wrap_content" - android:background="@drawable/volume_dialog_background" - android:divider="@drawable/volume_dialog_spacer" - android:gravity="center_horizontal" - android:orientation="vertical" - android:paddingVertical="@dimen/volume_dialog_vertical_padding" - android:showDividers="middle"> - - <FrameLayout - android:id="@+id/volume_dialog_ringer_button" - android:layout_width="@dimen/volume_dialog_button_size" - android:layout_height="@dimen/volume_dialog_button_size" /> - - <include - android:id="@+id/volume_dialog_slider" - layout="@layout/volume_dialog_slider" /> - - <Button - android:id="@+id/volume_dialog_settings" - android:layout_width="@dimen/volume_dialog_button_size" - android:layout_height="@dimen/volume_dialog_button_size" - android:background="@drawable/ripple_drawable_20dp" - android:contentDescription="@string/accessibility_volume_settings" - android:soundEffectsEnabled="false" - android:src="@drawable/horizontal_ellipsis" - android:tint="?androidprv:attr/materialColorPrimary" /> - </LinearLayout> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/contextual_edu_dialog.xml b/packages/SystemUI/res/layout/contextual_edu_dialog.xml index ee42b2363dd5..7eb6efe4afa4 100644 --- a/packages/SystemUI/res/layout/contextual_edu_dialog.xml +++ b/packages/SystemUI/res/layout/contextual_edu_dialog.xml @@ -40,6 +40,5 @@ android:fontFamily="google-sans-medium" android:maxWidth="280dp" android:textColor="?androidprv:attr/materialColorOnTertiaryFixed" - android:textSize="14sp" - android:textStyle="bold" /> + android:textSize="14sp" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml index 0029180932ee..2b40df47667a 100644 --- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml +++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml @@ -148,7 +148,7 @@ android:orientation="vertical" android:clickable="false" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:gravity="start|center_vertical"> <TextView android:id="@+id/mobile_title" diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml new file mode 100644 index 000000000000..7c59aad10c91 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml @@ -0,0 +1,82 @@ +<!-- + ~ 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 + --> + +<!-- Extends Framelayout --> +<com.android.systemui.statusbar.notification.footer.ui.view.FooterView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone"> + + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="12dp"> + + <TextView + android:id="@+id/unlock_prompt_footer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:drawablePadding="8dp" + android:gravity="center" + android:text="@string/unlock_to_see_notif_text" + android:textAppearance="?android:attr/textAppearanceButton" + android:visibility="gone" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.android.systemui.statusbar.notification.row.FooterViewButton + android:id="@+id/settings_button" + style="@style/TextAppearance.NotificationFooterButtonRedesign" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:background="@drawable/notif_footer_btn_background" + android:contentDescription="@string/notification_settings_button_description" + android:drawableStart="@drawable/notif_footer_btn_settings" + android:focusable="true" + app:layout_constraintStart_toStartOf="parent" /> + + <com.android.systemui.statusbar.notification.row.FooterViewButton + android:id="@+id/dismiss_text" + style="@style/TextAppearance.NotificationFooterButtonRedesign" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_marginHorizontal="8dp" + android:background="@drawable/notif_footer_btn_background" + android:contentDescription="@string/accessibility_clear_all" + android:focusable="true" + android:text="@string/clear_all_notifications_text" + app:layout_constraintEnd_toStartOf="@id/history_button" + app:layout_constraintStart_toEndOf="@id/settings_button" /> + + <com.android.systemui.statusbar.notification.row.FooterViewButton + android:id="@+id/history_button" + style="@style/TextAppearance.NotificationFooterButtonRedesign" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:background="@drawable/notif_footer_btn_background" + android:contentDescription="@string/notification_history_button_description" + android:drawableStart="@drawable/notif_footer_btn_history" + android:focusable="true" + app:layout_constraintEnd_toEndOf="parent" /> + </androidx.constraintlayout.widget.ConstraintLayout> + </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> +</com.android.systemui.statusbar.notification.footer.ui.view.FooterView> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index f77db956a493..3013e905e950 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -28,7 +28,6 @@ android:id="@+id/volume_dialog_floating_sliders_container" android:layout_width="wrap_content" android:layout_height="match_parent" - android:background="@drawable/volume_dialog_background" android:divider="@drawable/volume_dialog_floating_sliders_spacer" android:gravity="bottom" android:orientation="horizontal" @@ -36,6 +35,7 @@ android:showDividers="middle" /> <LinearLayout + android:id="@+id/volume_dialog" android:layout_width="@dimen/volume_dialog_width" android:layout_height="wrap_content" android:background="@drawable/volume_dialog_background" @@ -50,11 +50,9 @@ android:layout_width="@dimen/volume_dialog_button_size" android:layout_height="@dimen/volume_dialog_button_size" /> - <include - android:id="@+id/volume_dialog_slider" - layout="@layout/volume_dialog_slider" /> + <include layout="@layout/volume_dialog_slider" /> - <Button + <ImageButton android:id="@+id/volume_dialog_settings" android:layout_width="@dimen/volume_dialog_button_size" android:layout_height="@dimen/volume_dialog_button_size" diff --git a/packages/SystemUI/res/raw/trackpad_back_edu.json b/packages/SystemUI/res/raw/trackpad_back_edu.json index 908d26ff40cd..e705d4aef8a0 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-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 +{"v":"5.12.1","fr":60,"ip":96,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":79,"ix":3},"y":{"a":0,"k":197,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":459,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":459,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[32,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"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]}],"ix":3}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48,"ix":4}},{"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,"ix":7}},{"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]}],"ix":8}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12,"ix":9}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12,"ix":10}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12,"ix":11}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12,"ix":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,"ix":15}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0,"ix":16}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0,"ix":17}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0,"ix":18}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"k":[{"s":[0,0],"t":121,"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],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"IndieCorners Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":459,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":459,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":459,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":459,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":2,"l":2},"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]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":459,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":458,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":458,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":458,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277.5,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"a":0,"k":7,"ix":1},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":457,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,140,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,117.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,275,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[300,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 2","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 1","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":15,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[132,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets weather","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets clock","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039215686,0.811764705882,0.654901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":2,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":476,"ix":3},"y":{"a":0,"k":197,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":508,"st":0,"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":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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":2,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[-31.019,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"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]}],"ix":3}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48,"ix":4}},{"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,"ix":7}},{"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]}],"ix":8}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12,"ix":9}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12,"ix":10}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12,"ix":11}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12,"ix":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,"ix":15}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0,"ix":16}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0,"ix":17}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0,"ix":18}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"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],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"IndieCorners Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":7,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"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}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":10,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":8,"ix":1}},{"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"matte","parent":8,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":2,"l":2},"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]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"Back_LofiApp","parent":10,"tt":1,"tp":10,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":508,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":508,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":426,"op":902,"st":401,"ct":1,"bm":0}],"markers":[{"tm":96,"cm":"start","dr":0},{"tm":117,"cm":"gesture to R","dr":75},{"tm":192,"cm":"end progress R","dr":0},{"tm":543,"cm":"gesture to L","dr":75},{"tm":618,"cm":"end progress L","dr":0},{"tm":781,"cm":"launch","dr":75}],"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 deleted file mode 100644 index 56b6ff17d1f6..000000000000 --- a/packages/SystemUI/res/raw/trackpad_back_success.json +++ /dev/null @@ -1 +0,0 @@ -{"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/raw/trackpad_back_success_left.json b/packages/SystemUI/res/raw/trackpad_back_success_left.json new file mode 100644 index 000000000000..2e188652d862 --- /dev/null +++ b/packages/SystemUI/res/raw/trackpad_back_success_left.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":91,"w":554,"h":564,"nm":"Trackpad-JSON_BackGesture-rightSuccess","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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"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]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","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,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Back_RightDismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":2,"ty":3,"nm":"release Scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":476,"ix":3},"y":{"a":0,"k":197,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":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":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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-32.692,0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-32.692,0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":2,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[-31.019,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"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]}],"ix":3}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48,"ix":4}},{"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,"ix":7}},{"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]}],"ix":8}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12,"ix":9}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12,"ix":10}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12,"ix":11}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12,"ix":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,"ix":15}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0,"ix":16}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0,"ix":17}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0,"ix":18}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"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":[[-64,0],[-40,-24],[-24,-24],[0,0],[0,0],[-24,24],[-40,24],[-64,0]],"c":true}],"t":250,"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":[[-64,0],[-40,-24],[-24,-24],[0,0],[0,0],[-24,24],[-40,24],[-64,0]],"c":true}],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"k":[{"s":[0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"IndieCorners Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","parent":7,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-42,0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-42,0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":3,"nm":"pb:scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[242.5,197.5,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[242.5,197.5,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"k":[{"s":[80,80,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":340,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":9,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":10,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":8,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"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}}]}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"matte","parent":8,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":2,"l":2},"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]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"Back_LofiApp","parent":10,"tt":1,"tp":10,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":501,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":340,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":501,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,140,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,117.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,275,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[300,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 2","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 1","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":15,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[132,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets weather","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets clock","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039215686,0.811764705882,0.654901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":47,"op":91,"st":47,"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":42,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":430,"s":[100]},{"t":433,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[0,0],"t":41,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":90,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":41,"op":91,"st":41,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Back_RightDismiss","tt":1,"tp":4,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":0,"op":91,"st":-250,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":90,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_back_success_right.json b/packages/SystemUI/res/raw/trackpad_back_success_right.json new file mode 100644 index 000000000000..d5e4606622d2 --- /dev/null +++ b/packages/SystemUI/res/raw/trackpad_back_success_right.json @@ -0,0 +1 @@ +{"v":"5.12.1","fr":60,"ip":0,"op":91,"w":554,"h":564,"nm":"Trackpad-JSON_BackGesture-leftSuccess","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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"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]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","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,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":79,"ix":3},"y":{"a":0,"k":197,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[31.308,0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[31.308,0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.121568627656,0.211764708161,0.101960785687,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[32,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"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]}],"ix":3}},{"ty":0,"nm":"h","mn":"Pseudo/0.20784385308943532-0004","ix":4,"v":{"a":0,"k":48,"ix":4}},{"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,"ix":7}},{"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]}],"ix":8}},{"ty":0,"nm":"tl","mn":"Pseudo/0.20784385308943532-0009","ix":9,"v":{"a":0,"k":12,"ix":9}},{"ty":0,"nm":"tr","mn":"Pseudo/0.20784385308943532-0010","ix":10,"v":{"a":0,"k":12,"ix":10}},{"ty":0,"nm":"br","mn":"Pseudo/0.20784385308943532-0011","ix":11,"v":{"a":0,"k":12,"ix":11}},{"ty":0,"nm":"bl","mn":"Pseudo/0.20784385308943532-0012","ix":12,"v":{"a":0,"k":12,"ix":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,"ix":15}},{"ty":0,"nm":"Y Anchor %","mn":"Pseudo/0.20784385308943532-0016","ix":16,"v":{"a":0,"k":0,"ix":16}},{"ty":0,"nm":"X Position","mn":"Pseudo/0.20784385308943532-0017","ix":17,"v":{"a":0,"k":0,"ix":17}},{"ty":0,"nm":"Y Position ","mn":"Pseudo/0.20784385308943532-0018","ix":18,"v":{"a":0,"k":0,"ix":18}},{"ty":6,"nm":"","mn":"Pseudo/0.20784385308943532-0019","ix":19,"v":0}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"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],[40,-24],[64,0],[64,0],[40,24],[24,24],[0,0]],"c":true}],"t":250,"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],[40,-24],[64,0],[64,0],[40,24],[24,24],[0,0]],"c":true}],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"k":[{"s":[0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"IndieCorners Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[42,0,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[42,0,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[311.5,197.5,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[311.5,197.5,0],"t":340,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"k":[{"s":[80,80,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":340,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"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}}]}}]}],"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"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]}],"ix":2,"l":2},"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]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":340,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843152214,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,140,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450980619,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Back_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,117.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,275,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[300,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 2","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 1","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":15,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[132,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onTertiaryFixedVariant","cl":"onTertiaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets weather","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":15,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets clock","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":259,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843137255,0.301960784314,0.18431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039215686,0.811764705882,0.654901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.207843139768,0.301960796118,0.184313729405,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":47,"op":91,"st":47,"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":42,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":430,"s":[100]},{"t":433,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[0,0],"t":41,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0],"t":90,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.1254902035,0.027450982481,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":41,"op":91,"st":41,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Back_LeftDismiss","tt":1,"tp":4,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":0,"op":91,"st":-250,"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]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Back_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".tertiaryFixedDim","cl":"tertiaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":90,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.698039233685,0.811764717102,0.654901981354,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_home_edu.json b/packages/SystemUI/res/raw/trackpad_home_edu.json index 27db9fd752e3..94a138812a6f 100644 --- a/packages/SystemUI/res/raw/trackpad_home_edu.json +++ b/packages/SystemUI/res/raw/trackpad_home_edu.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":426,"w":554,"h":564,"nm":"Trackpad-JSON_HomeGesture-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Home_Dismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":2,"ty":3,"nm":"gesture:scale","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[277,197.321,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.13,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.036,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.921,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.779,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.606,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.39,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.122,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.786,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.354,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.78,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.975,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.883,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,190.304,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.897,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,187.507,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,186.208,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.036,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.998,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.082,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,182.274,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,181.557,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.918,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.344,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.824,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.353,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.924,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.532,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.174,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.843,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.538,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.256,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.995,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.752,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.527,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.319,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.124,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.943,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.776,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.619,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.474,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.339,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.213,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.095,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.985,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.884,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.789,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.62,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.476,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.353,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.209,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.039,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.212,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.896,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.197,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.536,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.4,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.939,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.375,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.791,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.751,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.459,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.006,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.442,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.798,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.092,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.339,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.546,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.721,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.87,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.995,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.191,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.378,0],"t":404,"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":3,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":4,"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":197,"s":[100]},{"t":203,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"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":195,"s":[28,28]},{"t":205,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,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":195,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":205,"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":195,"s":[28,28]},{"t":205,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,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":195,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":205,"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":195,"s":[28,28]},{"t":205,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,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":4,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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 1321317559","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":201,"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":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.321],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.13],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.036],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.921],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.779],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.606],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.39],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.122],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.786],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.354],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.781],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.975],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.883],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.652],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,190.304],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.897],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,187.507],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,186.208],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.036],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.998],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.082],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,182.274],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,181.557],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.918],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.344],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.824],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.353],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.924],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.532],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.174],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.843],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.538],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.256],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.995],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.752],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.528],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.319],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.124],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.943],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.776],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.619],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.474],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.339],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.213],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.095],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.985],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.638],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.587],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.09],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.793],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,201.516],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,207.702],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,212.767],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,217.041],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,220.728],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,223.965],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,226.837],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,229.392],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,231.662],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,233.68],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,235.467],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,237.042],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,238.421],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.622],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.66],"t":214,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.55],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.299],"t":216,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.916],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.407],"t":218,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.572],"t":220,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.29],"t":221,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.866],"t":222,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.351],"t":223,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.782],"t":224,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.175],"t":225,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.597],"t":226,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.08],"t":227,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.638],"t":228,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.281],"t":229,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.017],"t":230,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.165],"t":238,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.365],"t":240,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.555],"t":242,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.785],"t":245,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.579],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.389],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.278],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,234.833],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,221.896],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,215.604],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,211.894],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,209.347],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,207.439],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,205.933],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,204.711],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,203.696],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,202.839],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,202.106],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,201.474],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.925],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.444],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.022],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.649],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.32],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.03],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.776],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.552],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.355],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.183],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.034],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.902],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.787],"t":410,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.62],"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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.364705882353,0.258823529412,0,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":6,"ty":4,"nm":"matte","parent":2,"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":195,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":225,"s":[0,82.5,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[0,82.5,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":[0,49.5,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":200,"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":218,"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":232,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":252,"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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":7,"ty":0,"nm":"Home_LofiApp","parent":6,"tt":1,"tp":6,"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":195,"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":225,"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":8,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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":[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.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":50},"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":9,"ty":0,"nm":"Home_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":10,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","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.925490196078,0.752941176471,0.423529411765,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.925490196078,0.752941176471,0.423529411765,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":"Home_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":".onPrimaryFixed","cl":"onPrimaryFixed","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.149019607843,0.098039215686,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":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":".onPrimaryFixed","cl":"onPrimaryFixed","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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Home_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}]},"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}]},"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":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,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":"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":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,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":"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":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}]},"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":195,"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":201,"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":231,"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":".primaryFixedDim","cl":"primaryFixedDim","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.925490196078,0.752941176471,0.423529411765,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":0,"nm":"Home_Dismiss","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}],"markers":[],"props":{}}
\ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":426,"w":554,"h":564,"nm":"Trackpad-JSON_HomeGesture-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Home_Dismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":2,"ty":3,"nm":"gesture:scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[277,197.321,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.13,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.036,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.921,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.779,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.606,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.39,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.122,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.786,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.354,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.78,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.975,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.883,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,190.304,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.897,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,187.507,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,186.208,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.036,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.998,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.082,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,182.274,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,181.557,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.918,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.344,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.824,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.353,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.924,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.532,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.174,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.843,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.538,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.256,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.995,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.752,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.527,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.319,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.124,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.943,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.776,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.619,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.474,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.339,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.213,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.095,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.985,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.884,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.789,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.62,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.476,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.353,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.209,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.039,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.212,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.896,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.197,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.536,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.4,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.939,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.375,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.791,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.751,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.459,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.006,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.442,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.798,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.092,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.339,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.546,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.721,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.87,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.995,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.191,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.378,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":3,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":4,"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":197,"s":[100]},{"t":203,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":195,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":205,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":195,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":205,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":201,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":2,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.321],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.13],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.036],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.921],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.779],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.606],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.39],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,196.122],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.786],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,195.354],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,194.781],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.975],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,192.883],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,191.652],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,190.304],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,188.897],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,187.507],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,186.208],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.036],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.998],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,183.082],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,182.274],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,181.557],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.918],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,180.344],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.824],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,179.353],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.924],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.532],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.174],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.843],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.538],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,177.256],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.995],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.752],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.528],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.319],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,176.124],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.943],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.776],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.619],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.474],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.339],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.213],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.095],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.985],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.638],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.587],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.09],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.793],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,201.516],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,207.702],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,212.767],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,217.041],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,220.728],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,223.965],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,226.837],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,229.392],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,231.662],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,233.68],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,235.467],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,237.042],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,238.421],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.622],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.66],"t":214,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.55],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.299],"t":216,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.916],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.407],"t":218,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.572],"t":220,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.29],"t":221,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.866],"t":222,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.351],"t":223,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.782],"t":224,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.175],"t":225,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.597],"t":226,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.08],"t":227,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.638],"t":228,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.281],"t":229,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.017],"t":230,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.165],"t":238,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.365],"t":240,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.555],"t":242,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.785],"t":245,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.579],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.389],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.278],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,234.833],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,221.896],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,215.604],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,211.894],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,209.347],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,207.439],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,205.933],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,204.711],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,203.696],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,202.839],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,202.106],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,201.474],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.925],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.444],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,200.022],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.649],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.32],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,199.03],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.776],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.552],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.355],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.183],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,198.034],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.902],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.787],"t":410,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.62],"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":195,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":225,"s":[0,82.5,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[0,82.5,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":[0,49.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":416,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":200,"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":218,"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":232,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":252,"s":[0,0,0]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Home_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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":195,"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":225,"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"Home_LofiLauncher","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Home_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,140,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Home_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,117.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,275,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[300,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 2","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 1","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":15,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[132,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets weather","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets clock","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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":195,"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":201,"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":231,"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".primaryFixedDim","cl":"primaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Home_Dismiss","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":0,"op":426,"st":-25,"ct":1,"bm":0}],"markers":[{"tm":117,"cm":"drag with gesture","dr":53},{"tm":170,"cm":"release playback realtime","dr":80}],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_home_success.json b/packages/SystemUI/res/raw/trackpad_home_success.json index f14fde5a397e..8d0a339ba032 100644 --- a/packages/SystemUI/res/raw/trackpad_home_success.json +++ b/packages/SystemUI/res/raw/trackpad_home_success.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_HomeGesture-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadHome_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":".primaryFixedDim","cl":"primaryFixedDim","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.925490196078,0.752941176471,0.423529411765,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":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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":{"a":0,"k":[0.364705882353,0.258823529412,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":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Home_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":".onPrimaryFixed","cl":"onPrimaryFixed","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.149019607843,0.098039215686,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":"Message","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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.364705882353,0.258823529412,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":"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":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.5,140.5,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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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.149019607843,0.098039215686,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":"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":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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":"app only","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","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.364705882353,0.258823529412,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 1321317559","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadHome_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":".onPrimaryFixed","cl":"onPrimaryFixed","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":[277,197.5,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":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"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.149019607843,0.098039215686,0,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,"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":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"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":[1,0,0,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":"Home_LofiApp","tt":1,"tp":4,"refId":"comp_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":[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":6,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","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.925490196078,0.752941176471,0.423529411765,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.925490196078,0.752941176471,0.423529411765,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 +{"v":"5.12.1","fr":60,"ip":0,"op":91,"w":554,"h":564,"nm":"Trackpad-JSON_HomeGesture-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadHome_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"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]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Home_Dismiss","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":2,"ty":3,"nm":"gesture:scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[277,174.884,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.789,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.62,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.476,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.353,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.209,0],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,174.039,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"k":[{"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]}}],"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":3,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":4,"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":197,"s":[100]},{"t":203,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":195,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":205,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":195,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":205,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":195,"s":[28,28]},{"t":205,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":6,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":198,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":201,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":2,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,174.985],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,175.638],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,178.587],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,185.09],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,193.793],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,201.516],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,207.702],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,212.767],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,217.041],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,220.728],"t":204,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,223.965],"t":205,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,226.837],"t":206,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,229.392],"t":207,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,231.662],"t":208,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,233.68],"t":209,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,235.467],"t":210,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,237.042],"t":211,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,238.421],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.622],"t":213,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.66],"t":214,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.55],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.299],"t":216,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.916],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.407],"t":218,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.572],"t":220,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,243.29],"t":221,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.866],"t":222,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,242.351],"t":223,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.782],"t":224,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,241.175],"t":225,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.597],"t":226,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,240.08],"t":227,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.638],"t":228,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.281],"t":229,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.017],"t":230,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.165],"t":238,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.365],"t":240,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.555],"t":242,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,239.785],"t":245,"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.2,"y":0},"t":195,"s":[0,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":225,"s":[0,82.5,0],"h":1},{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":380,"s":[0,82.5,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":[0,49.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":416,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.28,"y":0},"t":200,"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":218,"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":232,"s":[0,1.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":252,"s":[0,0,0]}],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":195,"s":[504,315]},{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":225,"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":195,"s":[28]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":225,"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]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Home_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"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":195,"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":225,"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]}],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[503.5,314.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"Home_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":285,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Home_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,140,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Home_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,117.5,0],"ix":2,"l":2},"a":{"a":0,"k":[252,275,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 4","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 3","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[300,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 2","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"hotseat - 1","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":15,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[132,275],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"qsb","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets weather","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"widgets clock","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":195,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":204,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":389,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-29.497,0],"ix":2,"l":2},"a":{"a":0,"k":[252,128.003,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 7","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"apps","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","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,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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":195,"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":201,"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":231,"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]}],"ix":6,"l":2}},"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,"ix":1}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100,"ix":2}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"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":".primaryFixedDim","cl":"primaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onPrimaryFixedVariant","cl":"onPrimaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705882353,0.258823529412,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadHome_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":47,"op":91,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onPrimaryFixed","cl":"onPrimaryFixed","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":42,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":430,"s":[100]},{"t":433,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":41,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":90,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.149019607843,0.098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":41,"op":91,"st":41,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":90,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Home_Dismiss","tt":1,"tp":4,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":0,"op":91,"st":-195,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":90,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.925490196078,0.752941176471,0.423529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json index c2e945d38a70..10768fccd4ad 100644 --- a/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json +++ b/packages/SystemUI/res/raw/trackpad_recent_apps_edu.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":0},"y":{"a":0,"k":0}},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}]}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"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,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}]},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":0},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,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]}]}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"s":true,"x":{"a":0,"k":252},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"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,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":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":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0]},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"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":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","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":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,-30.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[176.678,176.678,100]}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","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":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":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}]}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"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":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}]},"p":{"a":0,"k":[0,0]},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}]},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"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":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,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":[115.3,72.35]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":13.78},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[167,15,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 7511","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[167,15]},"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 - 5","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[139,15,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 7508","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[139,15]},"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}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[111,15,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 7507","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[111,15]},"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}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[83,15,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 7506","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[83,15]},"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}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[55,15,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 7505","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[55,15]},"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":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"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":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":1},"lc":2,"lj":1,"ml":4,"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":"divider","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"a":{"a":0,"k":[6.826,6.826,0]},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 12","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501]},"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":"Ellipse 12","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 11","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.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":"Ellipse 11","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"Ellipse 5","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.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":"Ellipse 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true}},"nm":"Path 1","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true}},"nm":"Path 2","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0.4},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,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":"icon","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326]},"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":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[91,15,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":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":32.672},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,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":"Taskbar Lofi","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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":".onSecondaryFixed","cl":"onSecondaryFixed","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.145098039216,0.101960784314,0.01568627451,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":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.5,140.5,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":2,"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":250,"s":[100]},{"t":256,"s":[0]}]},"r":{"a":0,"k":0},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"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":248,"s":[28,28]},{"t":258,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,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":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"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":248,"s":[28,28]},{"t":258,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,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":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"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":248,"s":[28,28]},{"t":258,"s":[36,36]}]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,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":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.32549020648,0.270588248968,0.164705887437,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":600,"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":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Recents_EDU Loop","parent":4,"tt":1,"tp":4,"refId":"comp_0","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":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"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},{"ddd":0,"ind":8,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","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":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,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},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","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.850980392157,0.76862745098,0.627450980392,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":511,"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.850980392157,0.76862745098,0.627450980392,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":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":121,"cm":"start","dr":0},{"tm":142,"cm":"gesture","dr":75},{"tm":250,"cm":"release","dr":36},{"tm":356,"cm":"FLIP","dr":0},{"tm":392,"cm":"launch","dr":66}],"props":{}}
\ No newline at end of file +{"v":"5.12.1","fr":60,"ip":0,"op":511,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[0],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.001],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.002],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.003],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.004],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.006],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.008],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.01],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.012],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.016],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.02],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.025],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.031],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.038],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.047],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.059],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.073],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.091],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.116],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.15],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.196],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.249],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.306],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.366],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.425],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.481],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.53],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.575],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.614],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.648],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.678],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.706],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.73],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.752],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.772],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.79],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.807],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.822],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.836],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.849],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.861],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.873],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.883],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.892],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.901],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.91],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.917],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.925],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.931],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.937],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.943],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.949],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.954],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.958],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.963],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.967],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.97],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.974],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.977],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.98],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.983],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.985],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.987],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.989],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.991],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.993],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.994],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.996],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.997],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.998],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.999],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.009],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.038],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.093],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.193],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.4],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.636],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.739],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.8],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.84],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.871],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.894],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.912],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.928],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.94],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.951],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.959],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.967],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.973],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.979],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.983],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.987],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.99],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.993],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.995],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.997],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[2.999],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,278,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,278.45,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,279.615,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,281.252,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,283.166,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,287.233,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,289.181,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,290.982,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,292.599,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,294.012,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,295.216,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,296.216,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.023,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,297.655,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.131,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.474,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.705,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.465,0],"t":212,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.226,0],"t":215,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":377,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298.382,0],"t":378,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,299.372,0],"t":379,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,300.764,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,302.391,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,305.848,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,307.504,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,309.035,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,310.409,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,311.611,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,312.634,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,313.483,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.169,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,314.706,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.112,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.403,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.717,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.474,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,315.192,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[26.984],"t":127,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":128,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.95],"t":129,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.921],"t":130,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.882],"t":131,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.83],"t":132,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.765],"t":133,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.685],"t":134,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.589],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.478],"t":136,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.349],"t":137,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.205],"t":138,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.072],"t":139,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.926],"t":140,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.764],"t":141,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.589],"t":142,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.397],"t":143,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.187],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.711],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.44],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.146],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.826],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.479],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.1],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.686],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.236],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.745],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.207],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.616],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.967],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.248],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.457],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.578],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.602],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.514],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.303],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[12.954],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[11.477],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[9.885],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[8.215],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[6.526],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[4.878],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[3.338],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0.659],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-0.475],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-1.485],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-2.388],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.192],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-3.911],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-4.556],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.136],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-5.662],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.135],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.563],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-6.951],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.303],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.622],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-7.913],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.175],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.413],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.628],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.823],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-8.998],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.155],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.296],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.42],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.531],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.627],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.711],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.783],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.843],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.893],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.933],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.963],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.984],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-9.996],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[157.385],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.28],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.128],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.026],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.901],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.75],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.564],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.335],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.054],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.706],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.275],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.73],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.03],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.103],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.8],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[150.035],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[148.047],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.867],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.589],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.341],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.241],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[137.346],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[135.666],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[134.185],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[132.878],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[131.718],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.684],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[129.755],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.916],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[128.155],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[127.462],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.829],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[126.249],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.715],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[125.221],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.765],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.343],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.951],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.587],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[123.249],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.934],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.641],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.369],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.114],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.877],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.657],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.452],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.26],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[121.082],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.918],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.764],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.623],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.492],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.371],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.261],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.158],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.065],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.98],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.903],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.835],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.718],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.629],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.51],"t":215,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.094],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.397],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[147.982],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[149.027],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[151.2],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[153.675],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[154.764],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.396],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[155.825],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.141],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.386],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.581],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.74],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.871],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[156.981],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.074],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.218],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[157.362],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[503.613,314.758],"t":144,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.134,314.459],"t":146,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.832,314.27],"t":147,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.464,314.04],"t":148,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.025,313.765],"t":149,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.487,313.429],"t":150,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.824,313.015],"t":151,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.023,312.514],"t":152,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.032,311.895],"t":153,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[497.818,311.136],"t":154,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.328,310.205],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[494.484,309.053],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[492.194,307.621],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[489.307,305.817],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[485.592,303.495],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.67,300.419],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[473.76,296.1],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[464.395,290.247],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[453.849,283.656],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[442.286,276.429],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[430.198,268.874],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[418.274,261.421],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[407.131,254.457],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[397.077,248.173],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[388.165,242.603],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[380.31,237.694],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[373.373,233.358],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[367.218,229.511],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[361.732,226.082],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[356.803,223.002],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[352.354,220.221],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[348.318,217.699],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[344.643,215.402],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[341.283,213.302],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[338.205,211.378],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[335.37,209.606],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[332.752,207.97],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[330.33,206.456],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[328.092,205.058],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[326.012,203.757],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[324.082,202.552],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[322.291,201.432],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[320.617,200.386],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[319.062,199.414],"t":188,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[317.618,198.512],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[316.267,197.667],"t":190,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[315.013,196.883],"t":191,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[313.845,196.153],"t":192,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[312.756,195.472],"t":193,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[311.738,194.837],"t":194,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[310.793,194.246],"t":195,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.921,193.7],"t":196,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[309.107,193.192],"t":197,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[308.359,192.724],"t":198,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.663,192.289],"t":199,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[307.02,191.888],"t":200,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[306.436,191.522],"t":201,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.891,191.182],"t":202,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.399,190.874],"t":203,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.946,190.591],"t":204,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.539,190.337],"t":205,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[304.178,190.112],"t":206,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.85,189.906],"t":207,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.555,189.722],"t":208,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.306,189.566],"t":209,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[303.082,189.427],"t":210,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.892,189.308],"t":211,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.617,189.135],"t":213,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.4,189],"t":250,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.264,178.29],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.222,179.514],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[293.538,183.461],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[305.714,191.071],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[327.48,204.675],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[372.758,232.974],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[424.317,265.198],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[447.009,279.381],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[460.167,287.605],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[469.103,293.19],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[475.697,297.31],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[480.788,300.492],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[484.853,303.033],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[488.172,305.107],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[490.906,306.816],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[493.198,308.249],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[495.121,309.451],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[496.752,310.47],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[498.133,311.333],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[499.301,312.063],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[500.29,312.681],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.123,313.202],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[501.814,313.634],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.391,313.994],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[502.861,314.288],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.238,314.524],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[503.53,314.706],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[27.974],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.959],"t":145,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.942],"t":146,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.922],"t":147,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.898],"t":148,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.869],"t":149,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.833],"t":150,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.789],"t":151,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.736],"t":152,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.67],"t":153,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.589],"t":154,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.49],"t":155,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.368],"t":156,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.216],"t":157,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.024],"t":158,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.777],"t":159,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.45],"t":160,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.991],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.37],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.669],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.901],"t":164,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[23.098],"t":165,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.306],"t":166,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[21.566],"t":167,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.898],"t":168,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[20.306],"t":169,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.785],"t":170,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.324],"t":171,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.915],"t":172,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.551],"t":173,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[18.223],"t":174,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.928],"t":175,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.66],"t":176,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.416],"t":177,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[17.193],"t":178,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.988],"t":179,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.8],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.626],"t":181,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.465],"t":182,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.316],"t":183,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.178],"t":184,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.05],"t":185,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.931],"t":186,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.82],"t":187,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.717],"t":188,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.621],"t":189,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.531],"t":190,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.448],"t":191,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.37],"t":192,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.298],"t":193,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.23],"t":194,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.167],"t":195,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.11],"t":196,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.055],"t":197,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.006],"t":198,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.96],"t":199,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.917],"t":200,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.878],"t":201,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.842],"t":202,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.809],"t":203,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.779],"t":204,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.752],"t":205,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.728],"t":206,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.706],"t":207,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.687],"t":208,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.67],"t":209,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.655],"t":210,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.643],"t":211,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.633],"t":212,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.624],"t":213,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.61],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":380,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.907],"t":381,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.318],"t":382,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[15.109],"t":383,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[16.524],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[19.468],"t":385,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[22.82],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[24.295],"t":387,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.15],"t":388,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[25.731],"t":389,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.16],"t":390,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.491],"t":391,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.755],"t":392,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[26.971],"t":393,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.149],"t":394,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.298],"t":395,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.423],"t":396,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.529],"t":397,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.619],"t":398,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.694],"t":399,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.759],"t":400,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.813],"t":401,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.858],"t":402,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.895],"t":403,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.926],"t":404,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.95],"t":405,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.969],"t":406,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[27.993],"t":408,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[99.923,99.923,100],"t":144,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.828,99.828,100],"t":146,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.768,99.768,100],"t":147,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.695,99.695,100],"t":148,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.608,99.608,100],"t":149,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.501,99.501,100],"t":150,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.37,99.37,100],"t":151,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.211,99.211,100],"t":152,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.014,99.014,100],"t":153,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.773,98.773,100],"t":154,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.478,98.478,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.112,98.112,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.658,97.658,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.085,97.085,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.348,96.348,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.371,95.371,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94,94,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.142,92.142,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.049,90.049,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[87.755,87.755,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.357,85.357,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[82.991,82.991,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.78,80.78,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[78.785,78.785,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[77.017,77.017,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[75.458,75.458,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[74.082,74.082,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[72.861,72.861,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[71.772,71.772,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70.794,70.794,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.911,69.911,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[69.111,69.111,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.382,68.382,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.715,67.715,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[67.104,67.104,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.542,66.542,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[66.022,66.022,100],"t":181,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.542,65.542,100],"t":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[65.098,65.098,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.685,64.685,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.302,64.302,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.947,63.947,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.615,63.615,100],"t":187,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.306,63.306,100],"t":188,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.02,63.02,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.751,62.751,100],"t":190,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.503,62.503,100],"t":191,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.271,62.271,100],"t":192,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[62.055,62.055,100],"t":193,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.853,61.853,100],"t":194,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.665,61.665,100],"t":195,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.492,61.492,100],"t":196,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.331,61.331,100],"t":197,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.182,61.182,100],"t":198,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[61.044,61.044,100],"t":199,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.917,60.917,100],"t":200,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.801,60.801,100],"t":201,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.693,60.693,100],"t":202,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.595,60.595,100],"t":203,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.505,60.505,100],"t":204,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.424,60.424,100],"t":205,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.353,60.353,100],"t":206,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.288,60.288,100],"t":207,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.229,60.229,100],"t":208,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.18,60.18,100],"t":209,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.135,60.135,100],"t":210,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.098,60.098,100],"t":211,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.043,60.043,100],"t":213,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60,60,100],"t":250,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.6,56.6,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.989,56.989,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.242,58.242,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.657,60.657,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[64.976,64.976,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[73.96,73.96,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[84.19,84.19,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.692,88.692,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[91.303,91.303,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.076,93.076,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.384,94.384,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.394,95.394,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.201,96.201,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.859,96.859,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.402,97.402,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.857,97.857,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.238,98.238,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.562,98.562,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.836,98.836,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.068,99.068,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.264,99.264,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.429,99.429,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.566,99.566,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.681,99.681,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.774,99.774,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.849,99.849,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.907,99.907,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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":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":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":510,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[51.5,0,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.652,0,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.136,0,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.013,0,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.449,0,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.806,0,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.3,0,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.437,0,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[68.94,0,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[70.432,0,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.462,0,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.229,0,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[72.83,0,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.314,0,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.714,0,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.048,0,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.334,0,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.578,0,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.789,0,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[74.971,0,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.131,0,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.269,0,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.389,0,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.493,0,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.584,0,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.663,0,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.731,0,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.789,0,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.839,0,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.915,0,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.982,0,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.779,0,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[75.066,0,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[73.709,0,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[71.271,0,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[66.2,0,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[60.425,0,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.886,0,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.41,0,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.409,0,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.67,0,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[54.1,0,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.646,0,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[53.275,0,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.968,0,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.711,0,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.495,0,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.312,0,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.157,0,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[52.026,0,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.916,0,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.822,0,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.745,0,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.68,0,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.628,0,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.585,0,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.552,0,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[51.501,0,0],"t":409,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[123.341,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.654,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.223,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.146,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.662,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.549,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.838,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[134.455,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.421,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.083,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.576,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[136.962,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.272,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.528,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.742,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.924,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.08,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.216,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.334,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.437,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.527,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.606,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.734,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.869,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.864,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[138.402,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[137.527,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[135.96,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[132.701,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[129.002,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[127.358,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[126.406,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.763,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[125.288,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.923,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.633,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.396,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.199,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[124.034,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.895,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.776,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.675,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.589,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.516,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.403,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[123.299,15,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[104.041,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.182,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.436,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.844,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.517,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.808,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.265,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.981,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.703,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.923,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.092,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.228,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.341,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.436,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.517,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.587,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.648,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.746,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.853,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.956,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.938,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.73,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[110.34,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[109.649,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[108.197,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[106.559,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.828,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.403,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[105.117,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.906,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.745,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.616,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.511,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.424,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.35,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.288,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.19,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[104.091,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[84.704,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.639,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.537,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.371,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.048,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.684,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.505,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.398,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.324,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.271,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.195,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.123,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.045,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.068,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.166,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.338,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83.702,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.112,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.294,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.399,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.47,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.521,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.593,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[84.676,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[65.439,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.229,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.849,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.236,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.226,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.296,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.111,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[58.033,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.388,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.945,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.616,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.359,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[56.154,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.984,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.842,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.72,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.616,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.525,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.447,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.378,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.317,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.265,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.219,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.178,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.143,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.113,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.066,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.012,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.092,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.403,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55.986,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[57.027,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[59.212,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[61.67,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[62.762,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.396,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[63.825,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.141,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.385,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.578,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.736,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.867,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[64.977,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.07,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.149,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.217,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.274,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.323,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.364,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.426,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[65.491,15,0],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[51,15,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.913,15,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.615,15,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.073,15,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.194,15,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.751,15,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.001,15,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.869,15,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[40.328,15,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[39.409,15,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.778,15,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.309,15,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.941,15,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.645,15,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.402,15,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.199,15,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.025,15,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.876,15,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.747,15,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.635,15,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.536,15,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.451,15,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.376,15,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.31,15,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.253,15,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.203,15,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.161,15,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.124,15,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.093,15,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.068,15,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.047,15,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.017,15,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.129,15,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36.569,15,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[37.403,15,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[38.895,15,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[41.999,15,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[45.522,15,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.088,15,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[47.994,15,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[48.607,15,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.059,15,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.407,15,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.683,15,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[49.909,15,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.096,15,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.253,15,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.386,15,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.499,15,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.596,15,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.677,15,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.747,15,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.806,15,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.854,15,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.895,15,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.927,15,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[50.973,15,0],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2,-0.5],[2,-0.5]],"c":false}],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.019,-0.5],[2.019,-0.5]],"c":false}],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.077,-0.5],[2.077,-0.5]],"c":false}],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.185,-0.5],[2.185,-0.5]],"c":false}],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.361,-0.5],[2.361,-0.5]],"c":false}],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.65,-0.5],[2.65,-0.5]],"c":false}],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.2,-0.5],[3.2,-0.5]],"c":false}],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.829,-0.5],[3.829,-0.5]],"c":false}],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.136,-0.5],[4.136,-0.5]],"c":false}],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.318,-0.5],[4.318,-0.5]],"c":false}],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.444,-0.5],[4.444,-0.5]],"c":false}],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.538,-0.5],[4.538,-0.5]],"c":false}],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.612,-0.5],[4.612,-0.5]],"c":false}],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.671,-0.5],[4.671,-0.5]],"c":false}],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.761,-0.5],[4.761,-0.5]],"c":false}],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.796,-0.5],[4.796,-0.5]],"c":false}],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.826,-0.5],[4.826,-0.5]],"c":false}],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.852,-0.5],[4.852,-0.5]],"c":false}],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.874,-0.5],[4.874,-0.5]],"c":false}],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.894,-0.5],[4.894,-0.5]],"c":false}],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.91,-0.5],[4.91,-0.5]],"c":false}],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.925,-0.5],[4.925,-0.5]],"c":false}],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.938,-0.5],[4.938,-0.5]],"c":false}],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.949,-0.5],[4.949,-0.5]],"c":false}],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.959,-0.5],[4.959,-0.5]],"c":false}],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.967,-0.5],[4.967,-0.5]],"c":false}],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.974,-0.5],[4.974,-0.5]],"c":false}],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.98,-0.5],[4.98,-0.5]],"c":false}],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.99,-0.5],[4.99,-0.5]],"c":false}],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.996,-0.5],[4.996,-0.5]],"c":false}],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.973,-0.5],[4.973,-0.5]],"c":false}],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.887,-0.5],[4.887,-0.5]],"c":false}],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.72,-0.5],[4.72,-0.5]],"c":false}],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-4.421,-0.5],[4.421,-0.5]],"c":false}],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.8,-0.5],[3.8,-0.5]],"c":false}],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-3.093,-0.5],[3.093,-0.5]],"c":false}],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.782,-0.5],[2.782,-0.5]],"c":false}],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.601,-0.5],[2.601,-0.5]],"c":false}],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.479,-0.5],[2.479,-0.5]],"c":false}],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.388,-0.5],[2.388,-0.5]],"c":false}],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.318,-0.5],[2.318,-0.5]],"c":false}],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.263,-0.5],[2.263,-0.5]],"c":false}],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.217,-0.5],[2.217,-0.5]],"c":false}],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.18,-0.5],[2.18,-0.5]],"c":false}],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.148,-0.5],[2.148,-0.5]],"c":false}],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.122,-0.5],[2.122,-0.5]],"c":false}],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.099,-0.5],[2.099,-0.5]],"c":false}],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.081,-0.5],[2.081,-0.5]],"c":false}],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.064,-0.5],[2.064,-0.5]],"c":false}],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.051,-0.5],[2.051,-0.5]],"c":false}],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.039,-0.5],[2.039,-0.5]],"c":false}],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.03,-0.5],[2.03,-0.5]],"c":false}],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.022,-0.5],[2.022,-0.5]],"c":false}],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.01,-0.5],[2.01,-0.5]],"c":false}],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.006,-0.5],[2.006,-0.5]],"c":false}],"t":406,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.004,-0.5],[2.004,-0.5]],"c":false}],"t":407,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.001,-0.5],[2.001,-0.5]],"c":false}],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[0],"t":161,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[54.85],"t":162,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":163,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":384,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[0],"t":386,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-52.349,0.652,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.453,0.652,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.813,0.652,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.464,0.652,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.515,0.652,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-56.247,0.652,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-59.565,0.652,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.323,0.652,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-65.162,0.652,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.258,0.652,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.015,0.652,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-67.578,0.652,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.019,0.652,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.375,0.652,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.668,0.652,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.914,0.652,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.122,0.652,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.301,0.652,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.456,0.652,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.59,0.652,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.708,0.652,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.81,0.652,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.9,0.652,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.978,0.652,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.047,0.652,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.106,0.652,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.157,0.652,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.2,0.652,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.268,0.652,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.328,0.652,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":380,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.193,0.652,0],"t":381,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-69.662,0.652,0],"t":382,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-68.662,0.652,0],"t":383,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-66.874,0.652,0],"t":384,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-63.132,0.652,0],"t":385,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-58.906,0.652,0],"t":386,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-57.04,0.652,0],"t":387,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.956,0.652,0],"t":388,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-55.22,0.652,0],"t":389,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.678,0.652,0],"t":390,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-54.259,0.652,0],"t":391,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.925,0.652,0],"t":392,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.654,0.652,0],"t":393,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.43,0.652,0],"t":394,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.241,0.652,0],"t":395,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-53.082,0.652,0],"t":396,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.947,0.652,0],"t":397,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.831,0.652,0],"t":398,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.734,0.652,0],"t":399,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.65,0.652,0],"t":400,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.58,0.652,0],"t":401,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.522,0.652,0],"t":402,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.474,0.652,0],"t":403,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.435,0.652,0],"t":404,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.404,0.652,0],"t":405,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-52.354,0.652,0],"t":408,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[50,50,100],"t":155,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.309,50.309,100],"t":156,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.284,51.284,100],"t":157,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.079,53.079,100],"t":158,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.019,56.019,100],"t":159,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.828,60.828,100],"t":160,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[70,70,100],"t":161,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80.485,80.485,100],"t":162,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[85.597,85.597,100],"t":163,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[88.641,88.641,100],"t":164,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.739,90.739,100],"t":165,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[92.305,92.305,100],"t":166,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[93.53,93.53,100],"t":167,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[94.518,94.518,100],"t":168,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.335,95.335,100],"t":169,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.021,96.021,100],"t":170,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[96.603,96.603,100],"t":171,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.101,97.101,100],"t":172,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.531,97.531,100],"t":173,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[97.902,97.902,100],"t":174,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.226,98.226,100],"t":175,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.507,98.507,100],"t":176,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.753,98.753,100],"t":177,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.966,98.966,100],"t":178,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.152,99.152,100],"t":179,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.313,99.313,100],"t":180,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.451,99.451,100],"t":181,"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":182,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.671,99.671,100],"t":183,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.756,99.756,100],"t":184,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.826,99.826,100],"t":185,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.883,99.883,100],"t":186,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.982,99.982,100],"t":189,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":380,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[99.552,99.552,100],"t":381,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[98.109,98.109,100],"t":382,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[95.326,95.326,100],"t":383,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[90.35,90.35,100],"t":384,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[80,80,100],"t":385,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[68.215,68.215,100],"t":386,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[63.027,63.027,100],"t":387,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[60.02,60.02,100],"t":388,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.977,57.977,100],"t":389,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.47,56.47,100],"t":390,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[55.306,55.306,100],"t":391,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[54.377,54.377,100],"t":392,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[53.618,53.618,100],"t":393,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.993,52.993,100],"t":394,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.469,52.469,100],"t":395,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[52.03,52.03,100],"t":396,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.657,51.657,100],"t":397,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.341,51.341,100],"t":398,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[51.074,51.074,100],"t":399,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.848,50.848,100],"t":400,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.658,50.658,100],"t":401,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.5,50.5,100],"t":402,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.368,50.368,100],"t":403,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.26,50.26,100],"t":404,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.174,50.174,100],"t":405,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.107,50.107,100],"t":406,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.059,50.059,100],"t":407,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[50.024,50.024,100],"t":408,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[120,4],"t":155,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.383,4.161],"t":156,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.592,4.668],"t":157,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.818,5.601],"t":158,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[127.463,7.13],"t":159,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[133.427,9.631],"t":160,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[144.8,14.4],"t":161,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.801,19.852],"t":162,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[164.141,22.511],"t":163,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[167.915,24.093],"t":164,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.516,25.184],"t":165,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[172.458,25.999],"t":166,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[173.978,26.636],"t":167,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[175.203,27.15],"t":168,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.216,27.574],"t":169,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.065,27.931],"t":170,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[177.788,28.234],"t":171,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.406,28.493],"t":172,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[178.938,28.716],"t":173,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.399,28.909],"t":174,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.8,29.078],"t":175,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.149,29.224],"t":176,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.454,29.352],"t":177,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.718,29.463],"t":178,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[180.949,29.559],"t":179,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.148,29.643],"t":180,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.32,29.715],"t":181,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.467,29.777],"t":182,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.592,29.829],"t":183,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.697,29.873],"t":184,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.784,29.91],"t":185,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.855,29.939],"t":186,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.909,29.962],"t":187,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.978,29.991],"t":189,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":380,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[181.445,29.767],"t":381,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[179.655,29.017],"t":382,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[176.204,27.569],"t":383,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[170.034,24.982],"t":384,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[157.2,19.6],"t":385,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[142.586,13.472],"t":386,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[136.154,10.774],"t":387,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[132.424,9.21],"t":388,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[129.891,8.148],"t":389,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[128.022,7.364],"t":390,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[126.579,6.759],"t":391,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[125.427,6.276],"t":392,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[124.487,5.881],"t":393,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.712,5.556],"t":394,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[123.062,5.284],"t":395,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.517,5.055],"t":396,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[122.055,4.862],"t":397,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.663,4.697],"t":398,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.332,4.559],"t":399,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[121.051,4.441],"t":400,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.815,4.342],"t":401,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.62,4.26],"t":402,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.456,4.191],"t":403,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.323,4.135],"t":404,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.216,4.091],"t":405,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.133,4.056],"t":406,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.073,4.03],"t":407,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.03,4.013],"t":408,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[120.008,4.003],"t":409,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"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":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,29.984,0],"t":127,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.965,0],"t":128,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.936,0],"t":129,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.894,0],"t":130,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.84,0],"t":131,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.77,0],"t":132,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.682,0],"t":133,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.574,0],"t":134,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.445,0],"t":135,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.294,0],"t":136,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,29.121,0],"t":137,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.925,0],"t":138,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.746,0],"t":139,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.548,0],"t":140,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.33,0],"t":141,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,28.092,0],"t":142,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.832,0],"t":143,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.548,0],"t":144,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,27.239,0],"t":145,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.903,0],"t":146,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.536,0],"t":147,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,26.14,0],"t":148,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.709,0],"t":149,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,25.241,0],"t":150,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.73,0],"t":151,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,24.171,0],"t":152,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,23.563,0],"t":153,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.898,0],"t":154,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,22.171,0],"t":155,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,21.373,0],"t":156,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,20.496,0],"t":157,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,19.524,0],"t":158,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,18.451,0],"t":159,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,17.263,0],"t":160,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,15.943,0],"t":161,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,14.475,0],"t":162,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,12.841,0],"t":163,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,11.018,0],"t":164,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,9.023,0],"t":165,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,6.87,0],"t":166,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,4.614,0],"t":167,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,2.333,0],"t":168,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,0.106,0],"t":169,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-1.975,0],"t":170,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-3.877,0],"t":171,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-5.591,0],"t":172,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-7.125,0],"t":173,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-8.492,0],"t":174,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-9.714,0],"t":175,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-10.799,0],"t":176,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-11.771,0],"t":177,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-12.643,0],"t":178,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-13.428,0],"t":179,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.138,0],"t":180,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-14.777,0],"t":181,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.355,0],"t":182,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-15.879,0],"t":183,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.354,0],"t":184,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-16.784,0],"t":185,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.177,0],"t":186,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.532,0],"t":187,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-17.854,0],"t":188,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.146,0],"t":189,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.409,0],"t":190,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.645,0],"t":191,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-18.858,0],"t":192,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.048,0],"t":193,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.217,0],"t":194,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.366,0],"t":195,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.496,0],"t":196,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.61,0],"t":197,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.707,0],"t":198,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.788,0],"t":199,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.856,0],"t":200,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.911,0],"t":201,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.954,0],"t":202,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-19.984,0],"t":203,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":511,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}],"markers":[{"tm":142,"cm":"drag with gesture","dr":108},{"tm":217,"cm":"onPause","dr":0},{"tm":250,"cm":"release playback realtime","dr":36}],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json index bec6f353f380..1703c41df33a 100644 --- a/packages/SystemUI/res/raw/trackpad_recent_apps_success.json +++ b/packages/SystemUI/res/raw/trackpad_recent_apps_success.json @@ -1 +1 @@ -{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_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":".secondaryFixedDim","cl":"secondaryFixedDim","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.850980392157,0.76862745098,0.627450980392,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":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,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":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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":".onSecondaryFixed","cl":"onSecondaryFixed","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.145098039216,0.101960784314,0.01568627451,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":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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.325490196078,0.270588235294,0.164705882353,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":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[82.5,140.5,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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.145098039216,0.101960784314,0.01568627451,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":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.325490196078,0.270588235294,0.164705882353,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}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"TrackpadAK_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":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","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":[277,197.5,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":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"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.145098039216,0.101960784314,0.01568627451,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":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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.32549020648,0.270588248968,0.164705887437,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":600,"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":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Recents_LofiApp","tt":1,"tp":4,"refId":"comp_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":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","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,"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":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"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.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","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":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,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},{"ddd":0,"ind":8,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","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.850980392157,0.76862745098,0.627450980392,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.850980392157,0.76862745098,0.627450980392,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 +{"v":"5.12.1","fr":60,"ip":0,"op":97,"w":554,"h":564,"nm":"Trackpad-JSON_Recents-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}],"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[95.049,95.049,100],"ix":6,"l":2}},"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,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}],"ix":10},"p":{"a":0,"k":[81,127,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-0.289,"ix":10},"p":{"a":0,"k":[14.364,-33.591,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.125,0,0],"ix":1,"l":2},"s":{"a":0,"k":[104.744,104.744,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"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]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":11,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[95,95,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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]}],"ix":6,"l":2}},"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]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":88,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Checkbox - Widget","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"Trackpad-JSON_Recents-EDU","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":3,"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":250,"s":[100]},{"t":256,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-20,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-20,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":248,"s":[33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"right circle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","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":248,"s":[-33,0],"to":[0,0],"ti":[0,0]},{"t":258,"s":[-41,0]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"left circle","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","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":248,"s":[28,28]},{"t":258,"s":[36,36]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"size","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":37,"op":345,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Recents_EDU Loop","parent":5,"tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"Recents_EDU Loop","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"CNTL || playback","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"a":0,"k":0,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Picker","np":3,"mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ","ix":1,"en":1,"ef":[{"ty":7,"nm":"Menu","mn":"Pseudo/@@WcSiov6sT3a4/s0XPKYEOQ-0001","ix":1,"v":{"a":0,"k":2,"ix":1}}]},{"ty":5,"nm":"OUTPUT","np":3,"mn":"ADBE Slider Control","ix":2,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"k":[{"s":[1],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.009],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.038],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.093],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.193],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.4],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.636],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.739],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.8],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.84],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.871],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.894],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.912],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.928],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.94],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.951],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.959],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.967],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.973],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.979],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.983],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.987],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.99],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.993],"t":273,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.995],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.997],"t":275,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.998],"t":276,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[1.999],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}}]},{"ty":5,"nm":"Keys","np":3,"mn":"ADBE Slider Control","ix":3,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.831],"y":[0.109]},"o":{"x":[0.458],"y":[0.053]},"t":142,"s":[0]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.15],"y":[0.43]},"t":161,"s":[0.15]},{"t":217,"s":[1],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[1]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[1.4]},{"t":280,"s":[2],"h":1},{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":380,"s":[2]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":385,"s":[2.4]},{"t":410,"s":[3]}],"ix":1}}]},{"ty":5,"nm":"State (holds)","np":3,"mn":"ADBE Slider Control","ix":4,"en":1,"ef":[{"ty":0,"nm":"Slider","mn":"ADBE Slider Control-0001","ix":1,"v":{"a":0,"k":0,"ix":1}}]}],"shapes":[],"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"Null :: Taskbar drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[252,298.112,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[252,298,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Taskbar Lofi","parent":3,"refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":134,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":143,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":432,"s":[100]},{"t":444,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":0,"ix":3},"y":{"k":[{"s":[-10],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[-10],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[91,15,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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]}],"ix":1}}]}],"w":182,"h":30,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":3,"nm":"Focus Task :: Lift & Drop","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":252,"ix":3},"y":{"k":[{"s":[119.5],"t":250,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[119.746],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[120.54],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[122.071],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[124.808],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[130.5],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[136.982],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[139.835],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[141.489],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[142.613],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[143.442],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.082],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[144.593],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.01],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.354],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.642],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[145.884],"t":266,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.089],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.262],"t":268,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.409],"t":269,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.534],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.638],"t":271,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.725],"t":272,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[146.857],"t":274,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"matte","parent":5,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"k":[{"s":[302.247,188.904],"t":251,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[301.752,188.595],"t":252,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[300.798,187.999],"t":253,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[299.093,186.933],"t":254,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[295.546,184.716],"t":255,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[291.506,182.192],"t":256,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[289.729,181.08],"t":257,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[288.698,180.436],"t":258,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.998,179.999],"t":259,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.481,179.676],"t":260,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[287.082,179.427],"t":261,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.764,179.227],"t":262,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.504,179.065],"t":263,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.29,178.931],"t":264,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[286.11,178.819],"t":265,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.832,178.645],"t":267,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.555,178.472],"t":270,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[285.272,178.295],"t":278,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"k":[{"s":[14.603],"t":251,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.579],"t":252,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.532],"t":253,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.45],"t":254,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.278],"t":255,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[14.082],"t":256,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.996],"t":257,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.946],"t":258,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.912],"t":259,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.887],"t":260,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.868],"t":261,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.853],"t":262,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.84],"t":263,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.83],"t":264,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.821],"t":265,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.808],"t":267,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.794],"t":270,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[13.78],"t":278,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"Recents_LofiApp","parent":6,"tt":1,"tp":6,"refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[252,157.5,0],"ix":1,"l":2},"s":{"k":[{"s":[59.97,59.97,100],"t":251,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.871,59.871,100],"t":252,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.682,59.682,100],"t":253,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[59.344,59.344,100],"t":254,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[58.64,58.64,100],"t":255,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.839,57.839,100],"t":256,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.486,57.486,100],"t":257,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.281,57.281,100],"t":258,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.142,57.142,100],"t":259,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[57.04,57.04,100],"t":260,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.961,56.961,100],"t":261,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.898,56.898,100],"t":262,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.846,56.846,100],"t":263,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.804,56.804,100],"t":264,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.768,56.768,100],"t":265,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.713,56.713,100],"t":267,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.658,56.658,100],"t":270,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[56.602,56.602,100],"t":278,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"w":504,"h":315,"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":7,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"t":277,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,-30.035,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[176.678,176.678,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"second Tasks Zoom back","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"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":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":385,"s":[98,98,100]},{"t":410,"s":[95,95,100]}],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Null :: Reposition Side Task","parent":9,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-318.4,-38,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-277.34,-48.1,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,-63.25,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":12,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-111.72,0],"t":250,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-111.197,0],"t":251,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-109.514,0],"t":252,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-106.268,0],"t":253,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-100.462,0],"t":254,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-88.39,0],"t":255,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-74.643,0],"t":256,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-68.591,0],"t":257,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-65.083,0],"t":258,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-62.7,0],"t":259,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-60.943,0],"t":260,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-59.584,0],"t":261,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-58.5,0],"t":262,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-57.616,0],"t":263,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.886,0],"t":264,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-56.276,0],"t":265,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.762,0],"t":266,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-55.328,0],"t":267,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.96,0],"t":268,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.648,0],"t":269,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.385,0],"t":270,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-54.163,0],"t":271,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.977,0],"t":272,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.824,0],"t":273,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.698,0],"t":274,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.598,0],"t":275,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.521,0],"t":276,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.463,0],"t":277,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.424,0],"t":278,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":10,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.963},"t":217,"s":[-84.8,0,0],"to":[0,0,0],"ti":[0,0,0]},{"t":250,"s":[0,0,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.8,0.8],"y":[0.15,0.15]},"o":{"x":[0.3,0.3],"y":[0,0]},"t":250,"s":[302.4,189]},{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.05,0.05],"y":[0.7,0.7]},"t":255,"s":[227.56,142.34]},{"t":280,"s":[115.3,72.35]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[0.15]},"o":{"x":[0.3],"y":[0]},"t":250,"s":[14.6]},{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.05],"y":[0.7]},"t":255,"s":[14.272]},{"t":280,"s":[13.78]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":14,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":268,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":277,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[0,-53.175,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[0,-53.175,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":383,"s":[100]},{"t":392,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":0.15},"o":{"x":0.3,"y":0},"t":250,"s":[-411.95,20.325,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":255,"s":[-333.47,29.183,0],"to":[0,0,0],"ti":[0,0,0]},{"t":280,"s":[-215.75,42.47,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[115.3,72.35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":13.78,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":197,"op":511,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"Taskbar Lofi","fr":60,"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"app - 5","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[76,0,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[76,0,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[167,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7511","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[167,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 5","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"app - 4","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[139,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[139,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[139,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7508","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[139,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"app - 3","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[111,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[111,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[111,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7507","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[111,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 3","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"app - 2","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[83,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[83,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[83,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7506","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[83,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"app - 1","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[55,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[55,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[55,15,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 7505","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[55,15],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app - 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"divider","sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":90,"ix":10},"p":{"k":[{"s":[36,15,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[36,15,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"k":[{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5,-0.5],[5,-0.5]],"c":false}],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"divider","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":9,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":217,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":292,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0,"ix":10},"p":{"k":[{"s":[-70.349,0.652,0],"t":217,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[-70.349,0.652,0],"t":292,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}],"l":2},"a":{"a":0,"k":[6.826,6.826,0],"ix":1,"l":2},"s":{"k":[{"s":[100,100,100],"t":217,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}},{"s":[100,100,100],"t":292,"i":{"x":[1,1,1],"y":[1,1,1]},"o":{"x":[0,0,0],"y":[0,0,0]}}],"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,9.501],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 12","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[2.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 11","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.381,0],[0,1.381],[1.381,0],[0,-1.381]],"o":[[1.381,0],[0,-1.381],[-1.381,0],[0,1.381]],"v":[[0,2.5],[2.5,0],[0,-2.5],[-2.5,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[9.5,2.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.185,0.148],[0,0],[0,0],[0,0],[-0.086,0.24],[0,0.271],[0.468,0.462],[0.671,0],[0.468,-0.468],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.24,0.086]],"o":[[0,0],[0,0],[0,0],[0.148,-0.185],[0.086,-0.24],[0,-0.671],[-0.462,-0.468],[-0.671,0],[-0.462,0.462],[0,0.671],[0.468,0.462],[0.271,0],[0.24,-0.086]],"v":[[0.48,0.998],[2.809,3.326],[3.326,2.809],[0.998,0.48],[1.349,-0.157],[1.478,-0.924],[0.776,-2.624],[-0.924,-3.326],[-2.633,-2.624],[-3.326,-0.924],[-2.633,0.785],[-0.924,1.478],[-0.157,1.349]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462],[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462]],"o":[[-0.32,0.32],[-0.462,0],[-0.32,-0.326],[0,-0.462],[0.326,-0.326],[0.462,0],[0.326,0.32],[0,0.462]],"v":[[0.249,0.259],[-0.924,0.739],[-2.106,0.259],[-2.587,-0.924],[-2.106,-2.097],[-0.924,-2.587],[0.249,-2.097],[0.739,-0.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":5,"nm":"Merge Paths 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"st","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0.4,"ix":5},"lc":1,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":6,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[10.326,10.326],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"icon","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[91,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"k":[{"s":[182,30],"t":217,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}},{"s":[182,30],"t":292,"i":{"x":[1,1],"y":[1,1]},"o":{"x":[0,0],"y":[0,0]}}]},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":32.672,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Taskbar Lofi","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_4","nm":"Recents_LofiApp","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[339.937,151.75,0],"ix":2,"l":2},"a":{"a":0,"k":[339.937,151.75,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"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},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":9,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[481.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Triangle","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[457.874,21],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[292,25],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[334,279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Text field","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[109,28],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[425.5,208.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[160,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[400,158.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Sent","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[126,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":14,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[251,78.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Received","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[340,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":16,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82,171.125,0],"ix":2,"l":2},"a":{"a":0,"k":[82,171.125,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,177.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 4","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,165.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 3","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,171.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 2","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[82.5,140.5,0],"ix":2,"l":2},"a":{"a":0,"k":[82,140.938,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,22],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":39.375,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Search","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,31.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"header","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,257.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 6","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,245.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 5","np":1,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,251.375],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 3","np":1,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[132,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":12,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Message","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[82,171],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"block","np":1,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[64,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[80,96.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 2","np":1,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[92,8],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[94,84.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line 1","np":1,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":200,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Avatar","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,90.875],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"circle 1","np":1,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252,157.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"app only","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,459,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,128],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":18,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Frame 1321317559","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,198.5,0],"ix":2,"l":2},"a":{"a":0,"k":[95,95,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":190,"h":190,"ip":53,"op":97,"st":53,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"track matte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","tt":1,"tp":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":54,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":436,"s":[100]},{"t":439,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":47,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":96,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":47,"op":97,"st":47,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"track matte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":5,"refId":"comp_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":12,"op":58,"st":-235,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"track matte 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":511,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":0,"nm":"Trackpad-JSON_Recents-EDU","tt":1,"tp":7,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,282,0],"ix":2,"l":2},"a":{"a":0,"k":[277,282,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":554,"h":564,"ip":-217,"op":33,"st":-217,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"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":4,"ix":1}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1],"ix":4},"o":{"a":0,"k":50,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"illustrations: action key","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[277,197.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":28,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","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":96,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4,"ix":3},"ix":3,"mn":"ADBE Vector Filter - Offset","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"frame","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":221,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index f00ba9dad6c4..3f6825839c63 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeer met jou sleutelbord"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer kortpadsleutels"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeer met jou raakpaneel"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Verskaf deur apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Vertoon"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9f759fb43a43..c66d7a686f67 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"የቁልፍ ሰሌዳዎን በመጠቀም ያስሱ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"የቁልፍ ሰሌዳ አቋራጮችን ይወቁ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"የመዳሰሻ ሰሌዳዎን በመጠቀም ያስሱ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"በመተግበሪያዎች የቀረበ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ማሳያ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ያልታወቀ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index c2539a7ec689..c55948879b93 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"التنقّل باستخدام لوحة المفاتيح"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"تعرَّف على اختصارات لوحة المفاتيح"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"التنقّل باستخدام لوحة اللمس"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"مقدَّمة من التطبيقات"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"العرض"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"غير معروفة"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 240d29eaaf28..c006caa1a260 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"কীব’ৰ্ড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীব’ৰ্ডৰ শ্বৰ্টকাটসমূহৰ বিষয়ে জানক"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"এপে প্ৰদান কৰা"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিছপ্লে’"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজ্ঞাত"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index d3ebd7d6f43c..f33fa778c7ec 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dəstəyi çəkin"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviaturadan istifadə edərək hərəkət edin"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klaviatura qısayolları haqqında öyrənin"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Taçpeddən istifadə edərək hərəkət edin"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tətbiqlər tərəfindən təmin edilir"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displey"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Naməlum"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index a1fc84637a90..11db758db776 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -555,7 +555,7 @@ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prebaci ceo ekran"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada prebacujete ceo ekran, vidi se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada prebacujete aplikaciju, vidi se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string> - <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebacivanje ekrana"</string> + <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebacii ekran"</string> <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju koju želite da prebacite"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite da počnete da delite?"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kada delite, snimate ili prebacujete, Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za prevlačenje"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tasterskim prečicama"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću tačpeda"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Obezbeđuju aplikacije"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 526a11e0cb8f..8ba951ff5a02 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігацыя з дапамогай клавіятуры"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Азнаёмцеся са спалучэннямі клавіш"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігацыя з дапамогай сэнсарнай панэлі"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Забяспечваюцца праграмамі"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невядома"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index b843068e85d7..71649cf18bb4 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигирайте посредством клавиатурата си"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете за клавишните комбинации"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигирайте посредством сензорния панел"</string> @@ -1462,4 +1464,6 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Предоставено от приложения"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string> + <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Нулиране на панелите"</string> + <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Да се възстанови ли първоначалният ред и размери на панелите?"</string> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 0732387fe337..92056c9014f7 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"আপনার কীবোর্ড ব্যবহার করে নেভিগেট করুন"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"কীবোর্ড শর্টকাট সম্পর্কে জানুন"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"আপনার টাচপ্যাড ব্যবহার করে নেভিগেট করুন"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"অ্যাপের তরফ থেকে দেওয়া"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিসপ্লে"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজানা"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index b1146f4c482c..a02b604b8333 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ručica za prevlačenje"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tastature"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o prečicama tastature"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a836b97bfa79..7ea02369e608 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -129,8 +129,8 @@ <string name="screenrecord_share_label" msgid="5025590804030086930">"Comparteix"</string> <string name="screenrecord_save_title" msgid="1886652605520893850">"S\'ha desat la gravació de pantalla"</string> <string name="screenrecord_save_text" msgid="3008973099800840163">"Toca per veure-la"</string> - <string name="screenrecord_save_error" msgid="5862648532560118815">"S\'ha produït un error en desar la gravació de la pantalla"</string> - <string name="screenrecord_start_error" msgid="2200660692479682368">"S\'ha produït un error en iniciar la gravació de pantalla"</string> + <string name="screenrecord_save_error" msgid="5862648532560118815">"Hi ha hagut un error en desar la gravació de la pantalla"</string> + <string name="screenrecord_start_error" msgid="2200660692479682368">"Hi ha hagut un error en iniciar la gravació de pantalla"</string> <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Vols aturar la gravació?"</string> <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Ara mateix estàs gravant tota la pantalla"</string> <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ara mateix estàs gravant <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -159,8 +159,8 @@ <string name="issuerecord_share_label" msgid="3992657993619876199">"Comparteix"</string> <string name="issuerecord_save_title" msgid="4161043023696751591">"S\'ha desat la gravació del problema"</string> <string name="issuerecord_save_text" msgid="1205985304551521495">"Toca per veure"</string> - <string name="issuerecord_save_error" msgid="6913040083446722726">"S\'ha produït un error en desar la gravació del problema"</string> - <string name="issuerecord_start_error" msgid="3402782952722871190">"S\'ha produït un error en iniciar la gravació del problema"</string> + <string name="issuerecord_save_error" msgid="6913040083446722726">"Hi ha hagut un error en desar la gravació del problema"</string> + <string name="issuerecord_start_error" msgid="3402782952722871190">"Hi ha hagut un error en iniciar la gravació del problema"</string> <string name="immersive_cling_title" msgid="8372056499315585941">"Visualització en pantalla completa"</string> <string name="immersive_cling_description" msgid="2717426731830851921">"Per sortir, llisca cap avall des de la part superior de la pantalla"</string> <string name="immersive_cling_positive" msgid="3076681691468978568">"Entesos"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ansa per arrossegar"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega amb el teclat"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprèn les tecles de drecera"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega amb el ratolí tàctil"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionat per aplicacions"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconegut"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 70bdb6277343..750e75db8e10 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Úchyt pro přetažení"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigujte pomocí klávesnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte se klávesové zkratky"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigujte pomocí touchpadu"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytováno aplikacemi"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displej"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznámé"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index eeece4c4395b..b52b1823bdab 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtag"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger ved hjælp af dit tastatur"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Se tastaturgenveje"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger ved hjælp af din touchplade"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fra apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skærm"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukendt"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 95703148499b..ba6452f0da1c 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigation mit der Tastatur"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informationen zu Tastenkombinationen"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigation mit dem Touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Von Apps bereitgestellt"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 5551740f67d7..4cccb5ab4351 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Πλοήγηση με το πληκτρολόγιο"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Μάθετε συντομεύσεις πληκτρολογίου"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Πλοήγηση με την επιφάνεια αφής"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Παρέχεται από εφαρμογές"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Προβολή"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Άγνωστο"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index df9d2103b783..e96dd8c480b6 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 28cb9a311f5f..eee14f81ab50 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -1405,6 +1405,7 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> + <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> @@ -1462,4 +1463,6 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string> + <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reset tiles"</string> + <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Reset tiles to their original order and sizes?"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index df9d2103b783..e96dd8c480b6 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index df9d2103b783..e96dd8c480b6 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provided by apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index e39b2a48f9ee..97bd547dac94 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -554,7 +554,7 @@ <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir una app"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir pantalla entera"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando transmitas la pantalla entera, todo lo que se muestre es visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string> - <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando transmitas una app, todo lo que se muestre o reproduzcas en ella será visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string> + <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando transmitas una app, todo lo que se muestre o reproduzca en ella será visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string> <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir pantalla"</string> <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Elige la app para transmitir"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"¿Quieres empezar a compartir?"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega con el teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega con el panel táctil"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 6d77be4ff7cc..8d018bb35b55 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Desplázate con el teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende combinaciones de teclas"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Desplázate con el panel táctil"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Proporcionado por aplicaciones"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantalla"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index ed1440996b31..53651da18753 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -556,7 +556,7 @@ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kogu ekraanikuva ülekandmisel on kogu sellel kuvatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Rakenduse ülekandmisel on kogu rakenduses kuvatav või esitatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string> <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Kanna üle ekraanikuva"</string> - <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vali ülekandmiseks rakendus"</string> + <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Valige ülekandmiseks rakendus"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Kas alustada jagamist?"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kui jagate, salvestate või kannate üle, on Androidil juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string> <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kui jagate, salvestate või kannate rakendust üle, on Androidil juurdepääs kõigele, mida selles rakenduses kuvatakse või esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Õppige klaviatuuri otseteid"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeerige puuteplaadi abil"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Rakendustelt"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kuva"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Teadmata"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 2fb75d31c156..f7398628dbf0 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Arrastatzeko kontrol-puntua"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nabigatu teklatua erabilita"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ikasi lasterbideak"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nabigatu ukipen-panela erabilita"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Aplikazioenak"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Pantaila"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ezezagunak"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index b18c84ad3507..3d0b720e4951 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"پیمایش کردن بااستفاده از صفحهکلید"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"آشنایی با میانبرهای صفحهکلید"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"پیمایش کردن بااستفاده از صفحه لمسی"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ارائهشده از برنامهها"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"نمایشگر"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامشخص"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 46027419ec7f..ca6a97b0ec8c 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vetokahva"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Siirry käyttämällä näppäimistöä"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Opettele pikanäppäimiä"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Siirry käyttämällä kosketuslevyä"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Sovellusten tarjoama"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Näyttö"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tuntematon"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 77434a5fd283..4496b5ebfb16 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide de votre clavier"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Apprenez à utiliser les raccourcis-clavier"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournies par des applis"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Affichage"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index fa16862ac1d7..77b98eb11f74 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Poignée de déplacement"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviguer à l\'aide du clavier"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Découvrir les raccourcis clavier"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviguer à l\'aide de votre pavé tactile"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fournis par des applis"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Écran"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1d2c785a198e..3b94095c99b7 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navega co teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprende a usar os atallos de teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navega co panel táctil"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Provenientes de aplicacións"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Visualización"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Categoría descoñecida"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index c6014b18e4f6..f87184abcac3 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"કીબોર્ડ શૉર્ટકર્ટ જાણો"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"તમારા ટચપૅડ વડે નૅવિગેટ કરો"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ઍપ દ્વારા પ્રદાન કરવામાં આવેલી"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ડિસ્પ્લે"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"અજાણ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 28a4f2bc05eb..767f9df9aa83 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"कीबोर्ड का इस्तेमाल करके नेविगेट करें"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट के बारे में जानें"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचपैड का इस्तेमाल करके नेविगेट करें"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ऐप्लिकेशन से मिली जानकारी"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिसप्ले"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"कोई जानकारी नहीं है"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index cc19bea1fee0..f04045181686 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Marker za povlačenje"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Saznajte više o tipkovnim prečacima"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krećite se pomoću dodirne podloge"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Pružaju aplikacije"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Prikaz"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 9ee748bdc04c..629e2ff11c10 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Fogópont"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigáció a billentyűzet segítségével"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Billentyűparancsok megismerése"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigálás az érintőpaddal"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Alkalmazás által biztosított"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Kijelző"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ismeretlen"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index b07a78522437..92da53b839bd 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -312,7 +312,7 @@ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիոն"</string> <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Աուդիոյի փոխանցում"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"անցնել աուդիոյի փոխանցման կարգավորումներ"</string> - <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Այս սարքի երաժշտությունն ու տեսանյութերը երկու ականջակալներում էլ կնվագարկվեն"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Այս սարքի երաժշտությունն ու տեսանյութերը երկուսի ականջակալներում էլ կնվագարկվեն"</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Կիսվեք ձեր աուդիոյով"</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> և <xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>"</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Անցնել <xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g> սարքին"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Սովորեք օգտագործել ստեղնային դյուրանցումները"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Կողմնորոշվեք ձեր հպահարթակի օգնությամբ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Տրամադրվել են հավելվածների կողմից"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Էկրան"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Անհայտ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 84a45c6c8a08..d0544052126f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handel geser"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menavigasi menggunakan keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Pelajari pintasan keyboard"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Menavigasi menggunakan touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh aplikasi"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Tampilan"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f27790c05b1b..607f48fe5582 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Flettu með því að nota lyklaborðið"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Kynntu þér flýtilykla"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Flettu með því að nota snertiflötinn"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Frá forritum"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjár"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Óþekkt"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 7ace302022b6..0cbdb8688cdf 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Punto di trascinamento"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviga usando la tastiera"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Informazioni sulle scorciatoie da tastiera"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviga usando il touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Forniti dalle app"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Sconosciuti"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index b5783cf56688..465d8f5eaed9 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ניווט באמצעות המקלדת"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"מידע על מקשי קיצור"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ניווט באמצעות לוח המגע"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"מסופקים על ידי אפליקציות"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"מסך"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 8427f6709d31..6df9f73875ef 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"キーボードを使用して移動する"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"キーボード ショートカットの詳細"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"タッチパッドを使用して移動する"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"アプリから提供"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ディスプレイ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 3b0732da57c4..b30c7483e6e3 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ნავიგაცია კლავიატურის გამოყენებით"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"კლავიატურის მალსახმობების სწავლა"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ნავიგაცია სენსორული პანელის გამოყენებით"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"მოწოდებულია აპების მიერ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ეკრანი"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"უცნობი"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 65115583f681..c9517ec3ad21 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Пернетақтамен жұмыс істеңіз"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Перне тіркесімдерін үйреніңіз."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Сенсорлық тақтамен жұмыс істеңіз"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Қолданбалар ұсынған"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дисплей"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгісіз"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 7b7f912b3e1c..10a125b6e07c 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដងអូស"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ស្វែងយល់អំពីផ្លូវកាត់ក្ដារចុច"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"រុករកដោយប្រើផ្ទាំងប៉ះរបស់អ្នក"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ផ្ដល់ជូនដោយកម្មវិធី"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ផ្ទាំងបង្ហាញ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"មិនស្គាល់"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index c4417b7990b0..3ff0629835d6 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್ ಹ್ಯಾಂಡಲ್"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಕಲಿಯಿರಿ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ಆ್ಯಪ್ಗಳಿಂದ ಒದಗಿಸಲಾಗಿದೆ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ಡಿಸ್ಪ್ಲೇ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ಅಪರಿಚಿತ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 5602ee79eb37..0c1dc01e5dd9 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"키보드를 사용하여 이동"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"단축키 알아보기"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"터치패드를 사용하여 이동"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"앱에서 제공"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"디스플레이"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"알 수 없음"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 93777db15280..b74e4947034d 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Нерселерге баскычтоп аркылуу өтүңүз"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ыкчам баскычтар тууралуу билип алыңыз"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Нерселерге сенсордук такта аркылуу өтүңүз"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Колдонмолор сунуштады"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгисиз"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 07f3853b3c8b..ce8c95941fed 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ນຳທາງໂດຍໃຊ້ແປ້ນພິມຂອງທ່ານ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ສຶກສາຄີລັດ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ນຳທາງໂດຍໃຊ້ແຜ່ນສຳຜັດຂອງທ່ານ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ສະໜອງໃຫ້ໂດຍແອັບ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ການສະແດງຜົນ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ບໍ່ຮູ້ຈັກ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 605b8dd2e303..a7906b6ee184 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkimo rankenėlė"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naršykite naudodamiesi klaviatūra"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Sužinokite apie sparčiuosius klavišus"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naršykite naudodamiesi jutikline dalimi"</string> @@ -1462,4 +1464,6 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Teikia programos"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekranas"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nežinoma"</string> + <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Išklotinės nustatymas iš naujo"</string> + <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Iš naujo nustatyti išklotinės pradinę tvarką ir dydžius?"</string> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 0393ad50a619..cff7278df5a8 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Vilkšanas turis"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Pārvietošanās, izmantojot tastatūru"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Uzziniet par īsinājumtaustiņiem."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Pārvietošanās, izmantojot skārienpaliktni"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Nodrošina lietotnes"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Displejs"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 6b956ec6f532..464ef33c9b1f 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Движете се со користење на тастатурата"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Научете кратенки од тастатурата"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Движете се со користење на допирната подлога"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбедено од апликации"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index cd0a0442d3b0..08e979979482 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"കീബോർഡ് കുറുക്കുവഴികൾ മനസ്സിലാക്കുക"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"നിങ്ങളുടെ ടച്ച്പാഡ് ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ആപ്പുകൾ നൽകുന്നത്"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ഡിസ്പ്ലേ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"അജ്ഞാതം"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index e4f174641043..73b6aab3d665 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Гараа ашиглан шилжих"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Товчлуурын шууд холбоосыг мэдэж аваарай"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Мэдрэгч самбараа ашиглан шилжээрэй"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Аппуудаас өгсөн"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Дэлгэц"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Тодорхойгүй"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 458fe2cb3c84..22044c2817a6 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"तुमचा कीबोर्ड वापरून नेव्हिगेट करा"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"कीबोर्ड शॉर्टकट जाणून घ्या"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"तुमचा टचपॅड वापरून नेव्हिगेट करा"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"अॅप्सद्वारे पुरवलेले"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 8912366fbed4..7ee2dcdb54f3 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Pemegang seret"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigasi menggunakan papan kekunci anda"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Ketahui pintasan papan kekunci"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigasi menggunakan pad sentuh anda"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disediakan oleh apl"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Paparan"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index e3880017fde4..d3ccaf9cf635 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -555,7 +555,7 @@ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်ရန်"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"သင့်ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်သည့်အခါ ဖန်သားပြင်ပေါ်ရှိ အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"အက်ပ်တစ်ခုကို ကာစ်လုပ်သည့်အခါ ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string> - <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"စခရင် ကာစ်လုပ်ရန်"</string> + <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ဖန်သားပြင်ကာစ်လုပ်ရန်"</string> <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ကာစ်လုပ်ရန် အက်ပ်ရွေးခြင်း"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"စတင်မျှဝေမလား။"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ Android သည် သင့်ဖန်သားပြင်တွင် မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"သင့်ကီးဘုတ်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"လက်ကွက်ဖြတ်လမ်းများကို လေ့လာပါ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"သင့်တာ့ချ်ပက်ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"အက်ပ်များက ပံ့ပိုးထားသည်"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ဖန်သားပြင်"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"အမျိုးအမည်မသိ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 49c004b362e7..03e9f350ff86 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Håndtak"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Naviger med tastaturet"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lær deg hurtigtaster"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Naviger med styreflaten"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Levert av apper"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjerm"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukjent"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index e364e9ec3edf..4d165cc3a58f 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"किबोर्ड प्रयोग गरी नेभिगेट गर्नुहोस्"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"किबोर्डका सर्टकटहरू प्रयोग गर्न सिक्नुहोस्"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"एपले उपलब्ध गराएका"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"डिस्प्ले"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 0f8eb1146251..4249cb70b7ee 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handgreep voor slepen"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeren met je toetsenbord"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Leer sneltoetsen die je kunt gebruiken"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigeren met je touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Geleverd door apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Scherm"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 16127eb93ade..e2cd9ec8a114 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ଆପଣଙ୍କ କୀବୋର୍ଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"କୀବୋର୍ଡ ସର୍ଟକଟଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ଆପ୍ସ ଦ୍ୱାରା ପ୍ରଦାନ କରାଯାଇଛି"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ଡିସପ୍ଲେ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ଅଜଣା"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 63de1a6bda61..3c3102c838c2 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਬਾਰੇ ਜਾਣੋ"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ਐਪਾਂ ਵੱਲੋਂ ਮੁਹੱਈਆ ਕੀਤਾ ਗਿਆ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ਡਿਸਪਲੇ"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ਅਗਿਆਤ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index a5dbc06e2530..5b0fa56b3656 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Uchwyt do przeciągania"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Dowiedz się więcej o skrótach klawiszowych"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Nawiguj za pomocą touchpada"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Z aplikacji"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Wyświetlacz"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nieznane"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index da192e334e31..938602757904 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 127a97ea6ebc..fcd9bbab2c20 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -687,7 +687,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Áudio espacial"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desativar"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Corrigido"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Posição da cabeça"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toque para alterar o modo de campainha"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Indicador para arrastar"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue com o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos de teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue com o touchpad"</string> @@ -1462,4 +1464,6 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Disponibilizado por apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ecrã"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecido"</string> + <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Reponha os mosaicos"</string> + <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Repor os mosaicos para a ordem e os tamanhos originais?"</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index da192e334e31..938602757904 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Alça de arrastar"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navegue usando o teclado"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Aprenda atalhos do teclado"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navegue usando o touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Fornecidos por apps"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Exibição"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 5c97ab579183..42d24cdf66e5 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ghidaj de tragere"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navighează folosind tastatura"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Învață comenzile rapide de la tastatură"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navighează folosind touchpadul"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Oferite de aplicații"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ecran"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Necunoscută"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 5ae021def63e..cbb89c0efeee 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навигация с помощью клавиатуры"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Узнайте о сочетаниях клавиш."</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навигация с помощью сенсорной панели"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Приложения"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Экран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 03c2ba4379ce..1ff4316ac2f8 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ඔබේ යතුරු පුවරුව භාවිතයෙන් සංචාලනය කරන්න"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"යතුරුපුවරු කෙටිමං ඉගෙන ගන්න"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ඔබේ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"යෙදුම් මගින් සපයනු ලැබේ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"සංදර්ශකය"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"නොදනී"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ac80ae27c2d7..5f5ead1ded24 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Presúvadlo"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Prechádzajte pomocou klávesnice"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Naučte sa klávesové skratky"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Prechádzajte pomocou touchpadu"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Poskytnuté aplikáciami"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zobrazovanie"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznáme"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index f953a94dd5a5..3c027d964651 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krmarjenje s tipkovnico"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Učenje bližnjičnih tipk"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Krmarjenje s sledilno ploščico"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Zagotavljajo aplikacije"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Zaslon"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznano"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 0166acce341b..e6f84f44129e 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Doreza e zvarritjes"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigo duke përdorur tastierën tënde"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Mëso shkurtoret e tastierës"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigo duke përdorur bllokun me prekje"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Mundësuar nga aplikacionet"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekrani"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index d7075adc9a66..e82e2a2850df 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -555,7 +555,7 @@ <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Пребаци цео екран"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Када пребацујете цео екран, види се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Када пребацујете апликацију, види се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string> - <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребацивање екрана"</string> + <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребации екран"</string> <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Одаберите апликацију коју желите да пребаците"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Желите да почнете да делите?"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Када делите, снимате или пребацујете, Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Крећите се помоћу тастатуре"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Сазнајте више о тастерским пречицама"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Крећите се помоћу тачпеда"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Обезбеђују апликације"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index e03fc27b8cbf..06e46070b3d7 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handtag"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigera med tangentbordet"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Lär dig kortkommandon"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigera med styrplattan"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Tillhandahålls av appar"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skärm"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Okänt"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 0ab6f7d5000a..b8a9dc9abe82 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Kusogeza kwa kutumia kibodi yako"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Jifunze kuhusu mikato ya kibodi"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Vinavyotolewa na programu"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Maonyesho"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Visivyojulikana"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index f930d84dbfc8..b53c8ec286cf 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"கீபோர்டைப் பயன்படுத்திச் செல்லுதல்"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"கீபோர்டு ஷார்ட்கட்கள் குறித்துத் தெரிந்துகொள்ளுங்கள்"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"டச்பேடைப் பயன்படுத்திச் செல்லுதல்"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ஆப்ஸ் வழங்குபவை"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"டிஸ்ப்ளே"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"தெரியவில்லை"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 85973dfc1d85..d185851f5442 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"మీ కీబోర్డ్ ఉపయోగించి నావిగేట్ చేయండి"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"కీబోర్డ్ షార్ట్కట్ల గురించి తెలుసుకోండి"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"మీ టచ్ప్యాడ్ని ఉపయోగించి నావిగేట్ చేయండి"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"యాప్ల ద్వారా అందించబడినవి"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"డిస్ప్లే"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"తెలియదు"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 67006b6e6273..a2a68c1311f0 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"ดูข้อมูลเกี่ยวกับแป้นพิมพ์ลัด"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"ไปยังส่วนต่างๆ โดยใช้ทัชแพด"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ให้บริการโดยแอป"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"จอแสดงผล"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ไม่ทราบ"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e41c53f2fa5c..43490435999e 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Handle sa pag-drag"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Mag-navigate gamit ang iyong keyboard"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Matuto ng mga keyboard shortcut"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Mag-navigate gamit ang iyong touchpad"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Ibinibigay ng mga app"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Display"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Hindi Alam"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 1dab64adc32a..cf892e091882 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klavyenizi kullanarak gezinin"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Klavye kısayollarını öğrenin"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Dokunmatik alanınızı kullanarak gezinin"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Uygulamalar tarafından sağlanır"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index fcdccdb7e77d..307fc27d91e4 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -313,7 +313,7 @@ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Надсилання аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"відкрити налаштування надсилання аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_message" msgid="4251807313264307426">"Музика й відео із цього пристрою відтворюватимуться на обох парах навушників"</string> - <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Поділитися аудіо"</string> + <string name="quick_settings_bluetooth_audio_sharing_dialog_title" msgid="4406818346036286793">"Надіслати аудіо"</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_subtitle" msgid="3014906864710059236">"Пристрої \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\" і \"<xliff:g id="ACTIVE_DEVICE_NAME">%2$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_audio_sharing_dialog_switch_to_button" msgid="7546825655978203773">"Перемкнути на \"<xliff:g id="AVAILABLE_DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Навігація за допомогою клавіатури"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Комбінації клавіш: докладніше"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Навігація за допомогою сенсорної панелі"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Надано додатками"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Екран"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index a091d305b924..455848618bb2 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"اپنے کی بورڈ کا استعمال کر کے نیویگیٹ کریں"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"کی بورڈ شارٹ کٹس جانیں"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"اپنے ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"ایپس کے ذریعہ فراہم کردہ"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ڈسپلے"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامعلوم"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 51dcb29d3128..00c97b1e63ea 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Surish dastagi"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Klaviatura yordamida kezing"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tezkor tugmalar haqida"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Sensorli panel yordamida kezing"</string> @@ -1462,4 +1464,6 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Ilovalarga tegishli"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Ekran"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Noaniq"</string> + <string name="qs_edit_mode_reset_dialog_title" msgid="8841270491554460726">"Katakchalarni asliga qaytarish"</string> + <string name="qs_edit_mode_reset_dialog_content" msgid="8626426097929954027">"Katakchalar asl tartibi va oʻlchamiga qaytarilsinmi?"</string> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 98fc4679178a..6145f7cda6da 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Nút kéo"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Di chuyển bằng bàn phím"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Tìm hiểu về phím tắt"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Di chuyển bằng bàn di chuột"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Do các ứng dụng cung cấp"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Hiển thị"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Không xác định"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index cf80402a89d8..53ba1799b2be 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -441,7 +441,7 @@ <string name="zen_modes_dialog_title" msgid="8854640808100096934">"模式"</string> <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string> <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"设置"</string> - <string name="zen_mode_on" msgid="9085304934016242591">"已开启"</string> + <string name="zen_mode_on" msgid="9085304934016242591">"开启"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"已开启 • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"已关闭"</string> <string name="zen_mode_set_up" msgid="8231201163894922821">"未设置"</string> @@ -556,7 +556,7 @@ <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"投放整个屏幕时,屏幕上的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"投放单个应用时,该应用显示或播放的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string> <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放屏幕"</string> - <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"选择要投放的应用"</string> + <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"选择要投屏的应用"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"开始分享?"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"在分享内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string> <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"在分享、录制或投放内容时,Android 可以访问通过此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string> @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用键盘导航"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"了解键盘快捷键"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用触控板导航"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由应用提供"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"显示"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"未知"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 39cf982dd36e..f496c413ea43 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤導覽"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"瞭解鍵盤快速鍵"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板導覽"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index cbde8c3d3e8d..5157809a1b43 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"學習鍵盤快速鍵"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"使用觸控板操作"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"由應用程式提供"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"螢幕"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 2e668c5f732a..cd577e595722 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1405,6 +1405,8 @@ <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Hudula isibambi"</string> + <!-- no translation found for shortcut_helper_keyboard_settings_buttons_label (6720967595915985259) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Funa usebenzisa ikhibhodi yakho"</string> <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Funda izinqamuleli zamakhibhodi"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Funa usebenzisa iphedi yokuthinta"</string> @@ -1462,4 +1464,8 @@ <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Kuhlinzekwe ama-app"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Bonisa"</string> <string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Akwaziwa"</string> + <!-- no translation found for qs_edit_mode_reset_dialog_title (8841270491554460726) --> + <skip /> + <!-- no translation found for qs_edit_mode_reset_dialog_content (8626426097929954027) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 209a971814f4..87e6aa061a50 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1977,6 +1977,9 @@ slowing down. Also for tutorial it should be fine to lean to the side of being more strict rather than not strict enough and not teaching user the proper gesture as a result.--> <dimen name="touchpad_recent_apps_gesture_velocity_threshold">0.05dp</dimen> + <!-- Normal gesture threshold is system_gestures_distance_threshold but for tutorial we can + exaggerate gesture, which also works much better with live tracking --> + <dimen name="touchpad_tutorial_gestures_distance_threshold">48dp</dimen> <!-- Broadcast dialog --> <dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cdf15ca83dd9..0aa5ccf7a2b4 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1463,6 +1463,12 @@ <!-- The text for the notification history link. [CHAR LIMIT=40] --> <string name="manage_notifications_history_text">History</string> + <!-- The accessibility description for the notification settings button in the notification shade. --> + <string name="notification_settings_button_description">Notification settings</string> + + <!-- The accessibility description for the notification history button in the notification shade. --> + <string name="notification_history_button_description">Notification history</string> + <!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] --> <string name="notification_section_header_incoming">New</string> @@ -3745,6 +3751,10 @@ use. The helper shows shortcuts in categories, which can be collapsed or expanded. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_content_description_drag_handle">Drag handle</string> + <!-- Label on the keyboard settings button in keyboard shortcut helper, that allows user to + open keyboard settings while in shortcut helper. The helper is a component that shows the + user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_keyboard_settings_buttons_label">Keyboard Settings</string> <!-- Keyboard touchpad tutorial scheduler--> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index bda34530e6eb..ab45b9f1b5bc 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -785,6 +785,16 @@ <item name="android:minWidth">0dp</item> </style> + <style + name="TextAppearance.NotificationFooterButtonRedesign" + parent="@android:style/Widget.DeviceDefault.Button.Borderless"> + <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item> + <item name="android:drawableTint">?androidprv:attr/materialColorOnSurface</item> + <item name="android:textAllCaps">false</item> + <item name="android:textSize">16sp</item> + <item name="android:minWidth">0dp</item> + </style> + <style name="TextAppearance.HeadsUpStatusBarText" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info"> </style> @@ -1354,7 +1364,7 @@ <style name="InternetDialog.Network"> <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">88dp</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginStart">@dimen/internet_dialog_network_layout_margin</item> <item name="android:layout_marginEnd">@dimen/internet_dialog_network_layout_margin</item> <item name="android:layout_gravity">center_vertical|start</item> diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index e5cf7cf0eeb8..24d619119983 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -80,7 +80,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by @@ -140,7 +140,7 @@ constructor( val clockStr = clock.toString() loggers.forEach { it.d({ "New Clock: $str1" }) { str1 = clockStr } } - clock.initialize(resources, dozeAmount, 0f) + clock.initialize(isDarkTheme(), dozeAmount, 0f) if (!regionSamplingEnabled) { updateColors() @@ -244,30 +244,30 @@ constructor( private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) private var largeClockOnSecondaryDisplay = false + private fun isDarkTheme(): Boolean { + val isLightTheme = TypedValue() + context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) + return isLightTheme.data == 0 + } + private fun updateColors() { + val isDarkTheme = isDarkTheme() if (regionSamplingEnabled) { - clock?.let { clock -> - smallRegionSampler?.let { - val isRegionDark = it.currentRegionDarkness().isDark - clock.smallClock.events.onRegionDarknessChanged(isRegionDark) - } - - largeRegionSampler?.let { - val isRegionDark = it.currentRegionDarkness().isDark - clock.largeClock.events.onRegionDarknessChanged(isRegionDark) - } + clock?.smallClock?.run { + val isDark = smallRegionSampler?.currentRegionDarkness()?.isDark ?: isDarkTheme + events.onThemeChanged(theme.copy(isDarkTheme = isDark)) + } + clock?.largeClock?.run { + val isDark = largeRegionSampler?.currentRegionDarkness()?.isDark ?: isDarkTheme + events.onThemeChanged(theme.copy(isDarkTheme = isDark)) } return } - val isLightTheme = TypedValue() - context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) - val isRegionDark = isLightTheme.data == 0 - clock?.run { - Log.i(TAG, "Region isDark: $isRegionDark") - smallClock.events.onRegionDarknessChanged(isRegionDark) - largeClock.events.onRegionDarknessChanged(isRegionDark) + Log.i(TAG, "isThemeDark: $isDarkTheme") + smallClock.events.onThemeChanged(smallClock.theme.copy(isDarkTheme = isDarkTheme)) + largeClock.events.onThemeChanged(largeClock.theme.copy(isDarkTheme = isDarkTheme)) } } @@ -308,7 +308,6 @@ constructor( private val configListener = object : ConfigurationController.ConfigurationListener { override fun onThemeChanged() { - clock?.run { events.onColorPaletteChanged(resources) } updateColors() } @@ -505,7 +504,7 @@ constructor( largeRegionSampler?.stopRegionSampler() smallTimeListener?.stop() largeTimeListener?.stop() - clock?.apply { + clock?.run { smallClock.view.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener) largeClock.view.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener) } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index c7ea98052b66..11dde6aa0dfb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -465,7 +465,8 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onLayout(changed, l, t, r, b); // TODO: b/305022530 if (mClock != null && mClock.getConfig().getId().equals("DIGITAL_CLOCK_METRO")) { - mClock.getEvents().onColorPaletteChanged(mContext.getResources()); + mClock.getSmallClock().getEvents().onThemeChanged(mClock.getSmallClock().getTheme()); + mClock.getLargeClock().getEvents().onThemeChanged(mClock.getLargeClock().getTheme()); } if (changed) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 2d27f1c0ca73..6bcacd023c74 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -32,7 +32,6 @@ import static androidx.constraintlayout.widget.ConstraintSet.START; import static androidx.constraintlayout.widget.ConstraintSet.TOP; import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; -import static com.android.app.animation.InterpolatorsAndroidX.DECELERATE_QUINT; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import static java.lang.Integer.max; @@ -271,8 +270,7 @@ public class KeyguardSecurityContainer extends ConstraintLayout { public void onBackProgressed(BackEvent event) { float progress = event.getProgress(); // TODO(b/263819310): Update the interpolator to match spec. - float scale = MIN_BACK_SCALE - + (1 - MIN_BACK_SCALE) * (1 - DECELERATE_QUINT.getInterpolation(progress)); + float scale = MIN_BACK_SCALE + (1 - MIN_BACK_SCALE) * (1 - progress); setScale(scale); } }; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 83ab5245bc31..b3ea75d00917 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2368,7 +2368,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ 100, /* plugged= */ + mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ -1, /* plugged= */ 0, CHARGING_POLICY_DEFAULT, /* maxChargingWattage= */0, /* present= */true); // Watch for interesting updates diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 8ae11abab473..811b47d57c1d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -43,7 +43,7 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; import com.android.systemui.process.ProcessWrapper; import com.android.systemui.res.R; -import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.util.NotificationChannels; import java.lang.reflect.InvocationTargetException; @@ -454,13 +454,13 @@ public class SystemUIApplication extends Application implements @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { if (mServicesStarted) { - ConfigurationController configController = mSysUIComponent.getConfigurationController(); + ConfigurationForwarder configForwarder = mSysUIComponent.getConfigurationForwarder(); if (Trace.isEnabled()) { Trace.traceBegin( Trace.TRACE_TAG_APP, - configController.getClass().getSimpleName() + ".onConfigurationChanged()"); + configForwarder.getClass().getSimpleName() + ".onConfigurationChanged()"); } - configController.onConfigurationChanged(newConfig); + configForwarder.onConfigurationChanged(newConfig); Trace.endSection(); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt index 63791f9228c0..2d790f533965 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt @@ -36,7 +36,7 @@ import com.android.systemui.qs.tiles.ReduceBrightColorsTile import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.withContext @@ -127,7 +127,7 @@ constructor( private suspend fun getAccessibilityTileServices(context: Context): Set<ComponentName> = coroutineScope { val a11yServiceTileServices: Deferred<Set<ComponentName>> = - async(backgroundDispatcher) { + async(context = backgroundDispatcher) { manager.installedAccessibilityServiceList .mapNotNull { val packageName = it.resolveInfo.serviceInfo.packageName @@ -143,7 +143,7 @@ constructor( } val a11yShortcutInfoTileServices: Deferred<Set<ComponentName>> = - async(backgroundDispatcher) { + async(context = backgroundDispatcher) { manager .getInstalledAccessibilityShortcutListAsUser(context, context.userId) .mapNotNull { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt index fcb12068207e..ed5baa308ee1 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegate.kt @@ -31,7 +31,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Dialog for removing Extra Dim shortcuts. */ diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt index fd06bb174ea3..223a21db2016 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt @@ -50,7 +50,7 @@ import kotlin.math.hypot import kotlin.math.max import kotlin.math.min import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Monitor for tracking touches on the DreamOverlay to bring up the bouncer. */ class BouncerSwipeTouchHandler diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt index 9da9a3a98ef5..1951a23ebda7 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt @@ -34,7 +34,7 @@ import javax.inject.Inject import javax.inject.Named import kotlin.math.abs import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * [ShadeTouchHandler] is responsible for handling swipe down gestures over dream to bring down the diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index 732a90d2c01d..c9c4fd594adc 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -57,7 +57,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Defines interface for classes that can access authentication-related application state. */ diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 3080e1978b2a..67579fd7f696 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Hosts application business logic related to user authentication. @@ -320,7 +320,7 @@ constructor( } private suspend fun initiateGarbageCollection(delay: Duration) { - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { delay(delay) System.gc() System.runFinalization() diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt index 23045a336a5d..232b62985ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt @@ -35,7 +35,7 @@ import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Handles requests to go back either from a button or gesture. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt index c3e781800f9f..21569c13fab4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt @@ -32,7 +32,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.util.ViewController import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import java.io.PrintWriter /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index a1efc196dbee..2593cebb14d0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -73,7 +73,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val TAG = "UdfpsControllerOverlay" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt index 8c64b76af393..6c9be81ccaac 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractor.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Aggregates UI/device state that is not directly related to biometrics, but is often useful for diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index e7e8d8f80cda..2df5f16698b4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -64,7 +64,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val TAG = "BiometricViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 18446f02778a..02c378417f3d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -50,7 +50,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import kotlin.math.abs import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Helper for [BiometricViewBinder] to handle resize transitions. */ object BiometricViewSizeBinder { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt index 4ed786be99c9..e7a68ac2cfb9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt @@ -21,7 +21,7 @@ import com.android.systemui.res.R import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Sub-binder for the [CredentialPasswordView]. */ object CredentialPasswordViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt index eff698705042..29a189391054 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPatternViewBinder.kt @@ -9,7 +9,7 @@ import com.android.systemui.biometrics.ui.CredentialPatternView import com.android.systemui.biometrics.ui.CredentialView import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.lifecycle.repeatWhenAttached -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Sub-binder for the [CredentialPatternView]. */ object CredentialPatternViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt index 49f4b05e2cd9..3ea91f05827c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150 diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt index eab3b26e9b68..f6cf3d57a609 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt @@ -32,7 +32,7 @@ import com.android.systemui.util.kotlin.Quad import com.android.systemui.util.kotlin.Utils.Companion.toQuint import com.android.systemui.util.kotlin.sample import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val TAG = "PromptIconViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt index 9578da4238ee..8fe5ded939e2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt @@ -51,7 +51,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds the side fingerprint sensor indicator view to [SideFpsOverlayViewModel]. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt index a105d663424c..8763e5ccabe9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt @@ -25,7 +25,7 @@ import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @ExperimentalCoroutinesApi object UdfpsTouchOverlayBinder { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 168ba11309cc..0ac9405d0d8c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -72,7 +72,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** ViewModel for BiometricPrompt. */ class PromptViewModel diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt index 692a78be075f..13c72097c06e 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt @@ -63,6 +63,10 @@ constructor( logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing) when { + deviceItem.type == DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { + // Do nothing if the device is in audio sharing session + uiEventLogger.log(BluetoothTileDialogUiEvent.AUDIO_SHARING_DEVICE_CLICKED) + } deviceItem.type == DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { if (audioSharingQsDialogImprovement()) { diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt index 3ac942b769f4..c3df88e72de8 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogDelegate.kt @@ -28,7 +28,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class AudioSharingDialogDelegate @AssistedInject diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt index dc970aea7c41..9779db1a11ea 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDialogViewModel.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch sealed class AudioSharingDialogState { data object Hide : AudioSharingDialogState() diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt index 5c35c52a4327..b255a4f55220 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt @@ -56,7 +56,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ @@ -94,7 +94,7 @@ constructor( cancelJob() job = - coroutineScope.launch(mainDispatcher) { + coroutineScope.launch(context = mainDispatcher) { var updateDeviceItemJob: Job? var updateDialogUiJob: Job? = null val dialogDelegate = createBluetoothTileDialog() diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt index cf0f19f1d361..2b55e1c51f5f 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt @@ -44,9 +44,6 @@ constructor( disconnect() uiEventLogger.log(BluetoothTileDialogUiEvent.ACTIVE_DEVICE_DISCONNECT) } - DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { - uiEventLogger.log(BluetoothTileDialogUiEvent.AUDIO_SHARING_DEVICE_CLICKED) - } DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> { setActive() uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE) @@ -61,6 +58,7 @@ constructor( connect() uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT) } + DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { // Do nothing. Should already be handled in // AudioSharingDeviceItemActionInteractor. 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 7ed56296e865..292137377f55 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt @@ -42,8 +42,16 @@ abstract class DeviceItemFactory { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean + // Overloaded that defaults audioSharingAvailable to false + fun isFilterMatched( + context: Context, + cachedDevice: CachedBluetoothDevice, + audioManager: AudioManager, + ): Boolean = isFilterMatched(context, cachedDevice, audioManager, false) + abstract fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem companion object { @@ -80,6 +88,7 @@ internal open class ActiveMediaDeviceItemFactory : DeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return BluetoothUtils.isActiveMediaDevice(cachedDevice) && BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager) @@ -105,8 +114,9 @@ internal class AudioSharingMediaDeviceItemFactory( context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { - return BluetoothUtils.isAudioSharingEnabled() && + return audioSharingAvailable && BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, localBluetoothManager) } @@ -131,9 +141,10 @@ internal class AvailableAudioSharingMediaDeviceItemFactory( context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { - return BluetoothUtils.isAudioSharingEnabled() && - super.isFilterMatched(context, cachedDevice, audioManager) && + return audioSharingAvailable && + super.isFilterMatched(context, cachedDevice, audioManager, true) && BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice( cachedDevice, localBluetoothManager, @@ -160,6 +171,7 @@ internal class ActiveHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return BluetoothUtils.isActiveMediaDevice(cachedDevice) && BluetoothUtils.isAvailableHearingDevice(cachedDevice) @@ -171,6 +183,7 @@ open class AvailableMediaDeviceItemFactory : DeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return !BluetoothUtils.isActiveMediaDevice(cachedDevice) && BluetoothUtils.isAvailableMediaBluetoothDevice(cachedDevice, audioManager) @@ -195,6 +208,7 @@ internal class AvailableHearingDeviceItemFactory : AvailableMediaDeviceItemFacto context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return !BluetoothUtils.isActiveMediaDevice(cachedDevice) && BluetoothUtils.isAvailableHearingDevice(cachedDevice) @@ -206,6 +220,7 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && @@ -234,6 +249,7 @@ internal open class SavedDeviceItemFactory : DeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { !BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, cachedDevice.device) && @@ -263,6 +279,7 @@ internal class SavedHearingDeviceItemFactory : SavedDeviceItemFactory() { context: Context, cachedDevice: CachedBluetoothDevice, audioManager: AudioManager, + audioSharingAvailable: Boolean, ): Boolean { return if (Flags.enableHideExclusivelyManagedBluetoothDevice()) { !BluetoothUtils.isExclusivelyManagedBluetoothDevice( diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt index 01b84dac1065..1e0ba8e75714 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt @@ -50,6 +50,7 @@ class DeviceItemInteractor @Inject constructor( private val bluetoothTileDialogRepository: BluetoothTileDialogRepository, + private val audioSharingInteractor: AudioSharingInteractor, private val audioManager: AudioManager, private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter(), private val localBluetoothManager: LocalBluetoothManager?, @@ -76,7 +77,7 @@ constructor( object : BluetoothCallback { override fun onActiveDeviceChanged( activeDevice: CachedBluetoothDevice?, - bluetoothProfile: Int + bluetoothProfile: Int, ) { super.onActiveDeviceChanged(activeDevice, bluetoothProfile) logger.logActiveDeviceChanged(activeDevice?.address, bluetoothProfile) @@ -86,30 +87,27 @@ constructor( override fun onProfileConnectionStateChanged( cachedDevice: CachedBluetoothDevice, state: Int, - bluetoothProfile: Int + bluetoothProfile: Int, ) { super.onProfileConnectionStateChanged( cachedDevice, state, - bluetoothProfile + bluetoothProfile, ) logger.logProfileConnectionStateChanged( cachedDevice.address, state.toString(), - bluetoothProfile + bluetoothProfile, ) trySendWithFailureLogging(Unit, TAG, "onProfileConnectionStateChanged") } override fun onAclConnectionStateChanged( cachedDevice: CachedBluetoothDevice, - state: Int + state: Int, ) { super.onAclConnectionStateChanged(cachedDevice, state) - // Listen only when a device is disconnecting - if (state == 0) { - trySendWithFailureLogging(Unit, TAG, "onAclConnectionStateChanged") - } + trySendWithFailureLogging(Unit, TAG, "onAclConnectionStateChanged") } } localBluetoothManager?.eventManager?.registerCallback(listener) @@ -121,11 +119,19 @@ constructor( internal suspend fun updateDeviceItems(context: Context, trigger: DeviceFetchTrigger) { withContext(backgroundDispatcher) { val start = systemClock.elapsedRealtime() + val audioSharingAvailable = audioSharingInteractor.audioSharingAvailable() val deviceItems = bluetoothTileDialogRepository.cachedDevices .mapNotNull { cachedDevice -> deviceItemFactoryList - .firstOrNull { it.isFilterMatched(context, cachedDevice, audioManager) } + .firstOrNull { + it.isFilterMatched( + context, + cachedDevice, + audioManager, + audioSharingAvailable, + ) + } ?.create(context, cachedDevice) } .sort(deviceItemDisplayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices) @@ -136,13 +142,13 @@ constructor( logger.logDeviceFetch( JobStatus.FINISHED, trigger, - systemClock.elapsedRealtime() - start + systemClock.elapsedRealtime() - start, ) } else { logger.logDeviceFetch( JobStatus.CANCELLED, trigger, - systemClock.elapsedRealtime() - start + systemClock.elapsedRealtime() - start, ) } } @@ -150,7 +156,7 @@ constructor( private fun List<DeviceItem>.sort( displayPriority: List<DeviceItemType>, - mostRecentlyConnectedDevices: List<BluetoothDevice>? + mostRecentlyConnectedDevices: List<BluetoothDevice>?, ): List<DeviceItem> { return this.sortedWith( compareBy<DeviceItem> { displayPriority.indexOf(it.type) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 45e39caf6cff..92fcf39b41ad 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -38,7 +38,7 @@ import com.android.systemui.scene.domain.interactor.SceneBackInteractor import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt index aecfe1d2c9ad..355ce6a3798f 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt @@ -23,7 +23,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private val TAG = BouncerMessageAuditLogger::class.simpleName!! diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 6d6cd45eaa58..61cd7c7049f4 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt index ed931bd4e66a..1aaf4fb9f296 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Handles domain layer logic for locked sim cards. */ @@ -125,7 +125,7 @@ constructor( // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard // dismiss animation janky. - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { delay(5000) System.gc() System.runFinalization() @@ -151,7 +151,7 @@ constructor( PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE_UNAUDITED, UserHandle.SYSTEM ) - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { if (euiccManager != null) { euiccManager.switchToSubscription( INVALID_SUBSCRIPTION_ID, diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt index 87ec2549d1c2..fa57ab967f31 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/log/BouncerLoggerStartable.kt @@ -28,7 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Startable that logs the flows that bouncer depends on. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt index b8e6ad6956fe..0c36d0af7027 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt @@ -29,7 +29,7 @@ import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.bouncer.ui.BouncerMessageView import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.log.BouncerLogger -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object BouncerMessageViewBinder { @JvmStatic diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index 80c4291d2873..2dce28409b57 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -20,7 +20,7 @@ import com.android.systemui.util.kotlin.sample import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** View binder responsible for binding the compose version of the bouncer. */ object ComposeBouncerViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt index b76520b4ada1..71eda0c19e6f 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt @@ -38,7 +38,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.user.domain.interactor.SelectedUserInteractor import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds the bouncer container to its view model. */ object KeyguardBouncerViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt index 615cb5bb51c8..39a4f7021fde 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt @@ -24,7 +24,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class BouncerContainerViewModel @AssistedInject diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt index c383b8d5f95c..a6874b00ebfa 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt @@ -61,7 +61,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Holds UI state for the 2-line status message shown on the bouncer. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt index 67d312e17069..0d7be8c9c9af 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt @@ -47,7 +47,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Models UI state for the content of the bouncer scene. */ class BouncerSceneContentViewModel diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index b8c6ab3783d5..c0ea9ff598d1 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Holds UI state and handles user input for the password bouncer UI. */ class PasswordBouncerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 158f102ccdb3..633ce958eda7 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Holds UI state and handles user input for the pattern bouncer UI. */ class PatternBouncerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 5c8a9a692baf..5baec1e025e8 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Holds UI state and handles user input for the PIN code bouncer UI. */ class PinBouncerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt index 37d1887730b9..06d391704870 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt @@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -96,7 +96,7 @@ constructor( ) init { - applicationScope.launch(backgroundContext) { + applicationScope.launch(context = backgroundContext) { for (call in apiQueue) { val bounds = getMinMaxLinearBrightness() val value = call.value.clamp(bounds.first, bounds.second).floatValue diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index 470807985006..8639ee51b3f5 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -47,7 +47,7 @@ import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.utils.PolicyRestriction -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @Composable private fun BrightnessSlider( diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt index e83a825a0072..8814a1298e44 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardImageLoader.kt @@ -28,7 +28,7 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalOngoingContentStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalOngoingContentStartable.kt index b1b02cec871d..48a6d9de380c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalOngoingContentStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalOngoingContentStartable.kt @@ -25,7 +25,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class CommunalOngoingContentStartable diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 8510915d55af..4f9443efd857 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -62,7 +62,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt index 02bf9dbab373..258480e7fef5 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt @@ -39,7 +39,7 @@ import javax.inject.Named import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Callback that will be invoked when the Room database is created. Then the database will be diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt index 260dcbad6201..415fd4e2fe6a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates the state of communal mode. */ interface CommunalSceneRepository { diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt index 0e9428970a13..e164587ce02d 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates the state of widgets for communal mode. */ interface CommunalWidgetRepository { diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt index 905eda14e2d5..ec5540172be5 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * This class listens to [SceneTransitionLayout] transitions and manages keyguard transition @@ -73,7 +73,7 @@ constructor( private var progressJob: Job? = null private val currentToState: KeyguardState - get() = internalTransitionInteractor.currentTransitionInfoInternal.value.to + get() = internalTransitionInteractor.currentTransitionInfoInternal().to /** * The next keyguard state to trigger when exiting [CommunalScenes.Communal]. This is only used @@ -197,7 +197,7 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = internalTransitionInteractor.currentTransitionInfoInternal.value.to, + from = internalTransitionInteractor.currentTransitionInfoInternal().to, to = state, animator = null, modeOnCanceled = TransitionModeOnCanceled.REVERSE, @@ -273,7 +273,7 @@ constructor( } private suspend fun startTransitionToGlanceableHub() { - val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to + val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt index 2b7db148582d..6fa066306287 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformWhile -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates business-logic related to communal tutorial state. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt index f7cd2ab89140..f49394ad37bb 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.flow.takeWhile -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt index 0120b5c87f0a..9c754ea5f962 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt @@ -24,7 +24,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** View binder for communal tutorial indicator shown on keyguard. */ object CommunalTutorialIndicatorViewBinder { diff --git a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt index 00e5405dd904..00e5405dd904 100644 --- a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt 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 53109ac69fa9..7990450f6ac8 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 @@ -72,7 +72,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** The default view model used for showing the communal hub. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt index b46698ed87f2..4f9ed2f7dbf8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Communal app widget host that creates a [CommunalAppWidgetHostView]. */ class CommunalAppWidgetHost( diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt index 42107c1e9769..0b8d9775675f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Widget host that interacts with AppWidget service and host to bind and provide info for widgets 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 8c14d63c0e84..ca4bbc0ae3bd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -52,7 +52,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** An Activity for editing the widgets that appear in hub mode. */ class EditWidgetsActivity diff --git a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt index 813e0e025bf5..e6e474aea81d 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/ComposeInitializer.kt @@ -22,7 +22,7 @@ import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner -import com.android.compose.animation.ViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.android.systemui.lifecycle.ViewLifecycleOwner /** @@ -88,13 +88,13 @@ object ComposeInitializer { // Set the owners on the root. They will be reused by any ComposeView inside the root // hierarchy. root.setViewTreeLifecycleOwner(lifecycleOwner) - ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner) + root.setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner) } /** Function to be called on your window root view's [View.onDetachedFromWindow] function. */ fun onDetachedFromWindow(root: View) { (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy() root.setViewTreeLifecycleOwner(null) - ViewTreeSavedStateRegistryOwner.set(root, null) + root.setViewTreeSavedStateRegistryOwner(null) } } diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt index 5b1c9c8b8020..efbdf4d1533d 100644 --- a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt +++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt @@ -20,7 +20,7 @@ import com.android.app.tracing.coroutines.createCoroutineTracingContext import kotlin.coroutines.CoroutineContext fun newTracingContext(name: String): CoroutineContext { - return createCoroutineTracingContext(name) { className -> + return createCoroutineTracingContext(name, walkStackForDefaultNames = true) { className -> className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") || className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached") } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 3fe6669de556..17f1961e662c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -29,6 +29,7 @@ import com.android.systemui.people.PeopleProvider; import com.android.systemui.startable.Dependencies; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -125,13 +126,20 @@ public interface SysUIComponent { BootCompleteCacheImpl provideBootCacheImpl(); /** - * Creates a ContextComponentHelper. + * Creates a ConfigurationController. */ @SysUISingleton @GlobalConfig ConfigurationController getConfigurationController(); /** + * Creates a ConfigurationForwarder. + */ + @SysUISingleton + @GlobalConfig + ConfigurationForwarder getConfigurationForwarder(); + + /** * Creates a ContextComponentHelper. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 450863fb53c9..59c8f06a40ad 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -107,6 +107,7 @@ import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; +import com.android.systemui.shade.ShadeDisplayAwareModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -265,6 +266,7 @@ import javax.inject.Named; CommonSystemUIUnfoldModule.class, TelephonyRepositoryModule.class, TemporaryDisplayModule.class, + ShadeDisplayAwareModule.class, TouchpadModule.class, TunerModule.class, UserDomainLayerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt index 37c6e17de148..514242b41eb1 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt @@ -87,7 +87,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index dbd7f0739f6c..1a5654144b65 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Hosts application business logic related to device entry. diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt index 88daa5de8816..e81164b94d7c 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * FaceHelpMessageDeferral business logic. Processes face acquired and face help authentication diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt index f90f02aad892..a20556ca7e02 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Business logic for handling authentication events when an app is occluding the lockscreen. */ @ExperimentalCoroutinesApi diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt index 1fd7d009cee4..720a4fb2a5ab 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Triggers face auth and active unlock on lift when the device is showing the lock screen or diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt index e3fce0007f26..f4d256a5b2ba 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt @@ -27,7 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Provides per display instances of [CoroutineScope]. These will remain active as long as the diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt index 88d3a28669df..7253621237f6 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt @@ -32,7 +32,7 @@ import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Provides per display instances of [DisplayWindowProperties]. */ interface DisplayWindowPropertiesRepository { @@ -76,9 +76,7 @@ constructor( } override fun start() { - backgroundApplicationScope.launch( - CoroutineName("DisplayWindowPropertiesRepositoryImpl#start") - ) { + backgroundApplicationScope.launch("DisplayWindowPropertiesRepositoryImpl#start") { displayRepository.displayRemovalEvent.collect { removedDisplayId -> properties.row(removedDisplayId).clear() } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt index 2ce3e43389fa..ecddef6ee843 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt @@ -23,7 +23,7 @@ import java.io.PrintWriter import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Provides per display instances of [T]. */ interface PerDisplayStore<T> { @@ -76,11 +76,11 @@ abstract class PerDisplayStoreImpl<T>( } } - abstract fun createInstanceForDisplay(displayId: Int): T + protected abstract fun createInstanceForDisplay(displayId: Int): T override fun start() { val instanceType = instanceClass.simpleName - backgroundApplicationScope.launch(CoroutineName("PerDisplayStore#<$instanceType>start")) { + backgroundApplicationScope.launch("PerDisplayStore#<$instanceType>start") { displayRepository.displayRemovalEvent.collect { removedDisplayId -> val removedInstance = perDisplayInstances.remove(removedDisplayId) removedInstance?.let { onDisplayRemovalAction(it) } diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt index 62720a5b1377..7ba15a6cfd7d 100644 --- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Shows/hides a dialog to allow the user to decide whether to use the external display for @@ -93,11 +93,11 @@ constructor( bottomSheetFactory .createDialog( onStartMirroringClickListener = { - scope.launch(bgDispatcher) { pendingDisplay.enable() } + scope.launch(context = bgDispatcher) { pendingDisplay.enable() } dismissDialog() }, onCancelMirroring = { - scope.launch(bgDispatcher) { pendingDisplay.ignore() } + scope.launch(context = bgDispatcher) { pendingDisplay.ignore() } dismissDialog() }, navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom }, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 21922ff22afe..12718e8bd119 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -17,6 +17,7 @@ package com.android.systemui.doze; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.Flags.screenOffUnlockUdfps; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_QUICK_PICKUP; import static com.android.systemui.doze.DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS; @@ -248,8 +249,8 @@ public class DozeSensors { true /* touchscreen */, false /* ignoresSetting */, dozeParameters.longPressUsesProx(), - false /* immediatelyReRegister */, - true /* requiresAod */ + screenOffUnlockUdfps() /* immediatelyReRegister */, + !screenOffUnlockUdfps() /* requiresAod */ ), new PluginSensor( new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY), diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index 905174519245..694502484b36 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -45,7 +45,7 @@ import com.android.systemui.statusbar.CrossFadeHelper import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Controller for dream overlay animations. */ @DreamOverlayScope diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt index 724f1c5323cf..6b14ebf31d84 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class HomeControlsDreamService @Inject diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt index 14525269eb6b..03f58aca9fc6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt @@ -25,7 +25,7 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class HomeControlsDreamStartable @Inject diff --git a/packages/SystemUI/src/com/android/systemui/education/ContextualEducationMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/education/ContextualEducationMetricsLogger.kt new file mode 100644 index 000000000000..9af259a9b642 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/education/ContextualEducationMetricsLogger.kt @@ -0,0 +1,49 @@ +/* + * 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.education + +import com.android.systemui.contextualeducation.GestureType +import com.android.systemui.education.shared.model.EducationUiType +import com.android.systemui.shared.system.SysUiStatsLog +import javax.inject.Inject + +class ContextualEducationMetricsLogger @Inject constructor() { + fun logContextualEducationTriggered(gestureType: GestureType, uiType: EducationUiType) { + val statsGestureType = + when (gestureType) { + GestureType.BACK -> SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__GESTURE_TYPE__BACK + GestureType.HOME -> SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__GESTURE_TYPE__HOME + GestureType.OVERVIEW -> + SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__GESTURE_TYPE__OVERVIEW + GestureType.ALL_APPS -> + SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__GESTURE_TYPE__ALL_APPS + } + + val statsEducationType = + when (uiType) { + EducationUiType.Toast -> + SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__EDUCATION_TYPE__TOAST + EducationUiType.Notification -> + SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED__EDUCATION_TYPE__NOTIFICATION + } + SysUiStatsLog.write( + SysUiStatsLog.CONTEXTUAL_EDUCATION_TRIGGERED, + statsGestureType, + statsEducationType, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt index c88b36495ac2..d5876f211e57 100644 --- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Allows updating education data (e.g. signal count, shortcut time) for different gesture types. diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt index c17f3fb6dfe4..7242770e72e5 100644 --- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.ALL_APPS import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.education.ContextualEducationMetricsLogger import com.android.systemui.education.dagger.ContextualEducationModule.EduClock import com.android.systemui.education.data.model.GestureEduModel import com.android.systemui.education.shared.model.EducationInfo @@ -50,7 +51,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Allow listening to new contextual education triggered */ @SysUISingleton @@ -62,6 +63,7 @@ constructor( private val userInputDeviceRepository: UserInputDeviceRepository, private val tutorialRepository: TutorialSchedulerRepository, private val overviewProxyService: OverviewProxyService, + private val metricsLogger: ContextualEducationMetricsLogger, @EduClock private val clock: Clock, ) : CoreStartable { @@ -129,9 +131,11 @@ constructor( if (isUsageSessionExpired(it)) { contextualEducationInteractor.startNewUsageSession(it.gestureType) } else if (isEducationNeeded(it)) { + val educationType = getEduType(it) _educationTriggered.value = - EducationInfo(it.gestureType, getEduType(it), it.userId) + EducationInfo(it.gestureType, educationType, it.userId) contextualEducationInteractor.updateOnEduTriggered(it.gestureType) + metricsLogger.logContextualEducationTriggered(it.gestureType, educationType) } } } diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt index 1996efa14d7c..6baffdda4bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt @@ -37,7 +37,7 @@ import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutoria import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * A class to show contextual education on UI based on the edu produced from diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt index 906896fb920e..f54067a12ab6 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.transformLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Restarts the process after all passed in [Condition]s are true. */ class ConditionalRestarter @@ -60,7 +60,7 @@ constructor( private fun scheduleRestart(reason: String = "") { pendingReason = if (reason.isEmpty()) pendingReason else reason - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { combine(conditions.map { condition -> condition.canRestartNow }) { it.all { it } } // Once all conditions are met, delay. .transformLatest { allConditionsMet -> diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 47f0ecfb237a..3cc184d7ddf4 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -34,8 +34,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.shared.flag.DualShade -import com.android.systemui.statusbar.core.StatusBarConnectedDisplays -import com.android.systemui.statusbar.core.StatusBarSimpleFragment import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor @@ -75,8 +73,6 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha // Status bar chip dependencies statusBarCallChipNotificationIconToken dependsOn statusBarUseReposForCallChipToken statusBarCallChipNotificationIconToken dependsOn statusBarScreenSharingChipsToken - - StatusBarConnectedDisplays.token dependsOn StatusBarSimpleFragment.token } private inline val politeNotifications diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java index ccd69ca55f0c..4599afa66407 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/ShutdownUi.java @@ -36,10 +36,7 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.internal.R; -import com.android.settingslib.Utils; import com.android.systemui.scrim.ScrimDrawable; -import com.android.systemui.statusbar.BlurUtils; -import com.android.systemui.statusbar.phone.ScrimController; import javax.inject.Inject; @@ -49,13 +46,11 @@ import javax.inject.Inject; public class ShutdownUi { private Context mContext; - private BlurUtils mBlurUtils; private NearbyManager mNearbyManager; @Inject - public ShutdownUi(Context context, BlurUtils blurUtils, NearbyManager nearbyManager) { + public ShutdownUi(Context context, NearbyManager nearbyManager) { mContext = context; - mBlurUtils = blurUtils; mNearbyManager = nearbyManager; } @@ -63,25 +58,17 @@ public class ShutdownUi { * Display the shutdown UI. * @param isReboot Whether the device will be rebooting after this shutdown. * @param reason Cause for the shutdown. + * @return Shutdown dialog. */ - public void showShutdownUi(boolean isReboot, String reason) { + public Dialog showShutdownUi(boolean isReboot, String reason) { ScrimDrawable background = new ScrimDrawable(); final Dialog d = new Dialog(mContext, com.android.systemui.res.R.style.Theme_SystemUI_Dialog_GlobalActions); - d.setOnShowListener(dialog -> { - if (mBlurUtils.supportsBlursOnWindows()) { - int backgroundAlpha = (int) (ScrimController.BUSY_SCRIM_ALPHA * 255); - background.setAlpha(backgroundAlpha); - mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), - (int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255); - } else { - float backgroundAlpha = mContext.getResources().getFloat( - com.android.systemui.res.R.dimen.shutdown_scrim_behind_alpha); - background.setAlpha((int) (backgroundAlpha * 255)); - } - }); + float backgroundAlpha = mContext.getResources().getFloat( + com.android.systemui.res.R.dimen.shutdown_scrim_behind_alpha); + background.setAlpha((int) (backgroundAlpha * 255)); // Window initialization Window window = d.getWindow(); @@ -110,14 +97,9 @@ public class ShutdownUi { d.setContentView(getShutdownDialogContent(isReboot)); d.setCancelable(false); - int color; - if (mBlurUtils.supportsBlursOnWindows()) { - color = Utils.getColorAttrDefaultColor(mContext, - com.android.systemui.res.R.attr.wallpaperTextColor); - } else { - color = mContext.getResources().getColor( - com.android.systemui.res.R.color.global_actions_shutdown_ui_text); - } + int color = mContext.getResources().getColor( + com.android.systemui.res.R.color.global_actions_shutdown_ui_text, + mContext.getTheme()); ProgressBar bar = d.findViewById(R.id.progress); bar.getIndeterminateDrawable().setTint(color); @@ -136,6 +118,8 @@ public class ShutdownUi { } d.show(); + + return d; } /** @@ -162,7 +146,6 @@ public class ShutdownUi { } } - @StringRes @VisibleForTesting int getRebootMessage(boolean isReboot, @Nullable String reason) { if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt new file mode 100644 index 000000000000..215ceacaef14 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/StateAwareExpandable.kt @@ -0,0 +1,93 @@ +/* + * 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.haptics.msdl.qs + +import android.content.ComponentName +import android.view.ViewGroup +import com.android.systemui.animation.ActivityTransitionAnimator +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.animation.Expandable + +private fun ActivityTransitionAnimator.Controller.withStateAwareness( + onActivityLaunchTransitionStart: () -> Unit, + onActivityLaunchTransitionEnd: () -> Unit, +): ActivityTransitionAnimator.Controller { + val delegate = this + return object : ActivityTransitionAnimator.Controller by delegate { + override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { + onActivityLaunchTransitionStart() + delegate.onTransitionAnimationStart(isExpandingFullyAbove) + } + + override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) { + onActivityLaunchTransitionEnd() + delegate.onTransitionAnimationCancelled(newKeyguardOccludedState) + } + + override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) { + onActivityLaunchTransitionEnd() + delegate.onTransitionAnimationEnd(isExpandingFullyAbove) + } + } +} + +private fun DialogTransitionAnimator.Controller.withStateAwareness( + onDialogDrawingStart: () -> Unit, + onDialogDrawingEnd: () -> Unit, +): DialogTransitionAnimator.Controller { + val delegate = this + return object : DialogTransitionAnimator.Controller by delegate { + + override fun startDrawingInOverlayOf(viewGroup: ViewGroup) { + onDialogDrawingStart() + delegate.startDrawingInOverlayOf(viewGroup) + } + + override fun stopDrawingInOverlay() { + onDialogDrawingEnd() + delegate.stopDrawingInOverlay() + } + } +} + +fun Expandable.withStateAwareness( + onDialogDrawingStart: () -> Unit, + onDialogDrawingEnd: () -> Unit, + onActivityLaunchTransitionStart: () -> Unit, + onActivityLaunchTransitionEnd: () -> Unit, +): Expandable { + val delegate = this + return object : Expandable { + override fun activityTransitionController( + launchCujType: Int?, + cookie: ActivityTransitionAnimator.TransitionCookie?, + component: ComponentName?, + returnCujType: Int?, + ): ActivityTransitionAnimator.Controller? = + delegate + .activityTransitionController(launchCujType, cookie, component, returnCujType) + ?.withStateAwareness(onActivityLaunchTransitionStart, onActivityLaunchTransitionEnd) + + override fun dialogTransitionController( + cuj: DialogCuj? + ): DialogTransitionAnimator.Controller? = + delegate + .dialogTransitionController(cuj) + ?.withStateAwareness(onDialogDrawingStart, onDialogDrawingEnd) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt new file mode 100644 index 000000000000..79059506b727 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt @@ -0,0 +1,190 @@ +/* + * 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.haptics.msdl.qs + +import android.service.quicksettings.Tile +import com.android.systemui.Flags +import com.android.systemui.animation.Expandable +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel +import com.android.systemui.util.kotlin.pairwise +import com.google.android.msdl.data.model.MSDLToken +import com.google.android.msdl.domain.MSDLPlayer +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.transform + +/** A view-model to trigger haptics feedback on Quick Settings tiles */ +@OptIn(ExperimentalCoroutinesApi::class) +class TileHapticsViewModel +@AssistedInject +constructor( + private val msdlPlayer: MSDLPlayer, + @Assisted private val tileViewModel: TileViewModel, +) : ExclusiveActivatable() { + + private val tileInteractionState = MutableStateFlow(TileInteractionState.IDLE) + private val tileAnimationState = MutableStateFlow(TileAnimationState.IDLE) + private val canPlayToggleHaptics: Boolean + get() = + tileAnimationState.value == TileAnimationState.IDLE && + tileInteractionState.value == TileInteractionState.CLICKED + + val isIdle: Boolean + get() = + tileAnimationState.value == TileAnimationState.IDLE && + tileInteractionState.value == TileInteractionState.IDLE + + private val toggleHapticsState: Flow<TileHapticsState> = + tileViewModel.state + .mapLatest { it.state } + .pairwise() + .transform { (previous, current) -> + val toggleState = + when { + !canPlayToggleHaptics -> TileHapticsState.NO_HAPTICS + previous == Tile.STATE_INACTIVE && current == Tile.STATE_ACTIVE -> + TileHapticsState.TOGGLE_ON + previous == Tile.STATE_ACTIVE && current == Tile.STATE_INACTIVE -> + TileHapticsState.TOGGLE_OFF + else -> TileHapticsState.NO_HAPTICS + } + emit(toggleState) + } + .distinctUntilChanged() + + private val interactionHapticsState: Flow<TileHapticsState> = + combine(tileInteractionState, tileAnimationState) { interactionState, animationState -> + when { + interactionState == TileInteractionState.LONG_CLICKED && + animationState == TileAnimationState.ACTIVITY_LAUNCH -> + TileHapticsState.LONG_PRESS + interactionState == TileInteractionState.LONG_CLICKED && + !tileViewModel.currentState.handlesLongClick -> + TileHapticsState.FAILED_LONGPRESS + else -> TileHapticsState.NO_HAPTICS + } + } + .distinctUntilChanged() + + private val hapticsState: Flow<TileHapticsState> = + merge(toggleHapticsState, interactionHapticsState) + + override suspend fun onActivated(): Nothing { + try { + hapticsState.collect { hapticsState -> + val tokenToPlay: MSDLToken? = + when (hapticsState) { + TileHapticsState.TOGGLE_ON -> MSDLToken.SWITCH_ON + TileHapticsState.TOGGLE_OFF -> MSDLToken.SWITCH_OFF + TileHapticsState.LONG_PRESS -> MSDLToken.LONG_PRESS + TileHapticsState.FAILED_LONGPRESS -> MSDLToken.FAILURE + TileHapticsState.NO_HAPTICS -> null + } + tokenToPlay?.let { + msdlPlayer.playToken(it) + resetStates() + } + } + awaitCancellation() + } finally { + resetStates() + } + } + + private fun resetStates() { + tileInteractionState.value = TileInteractionState.IDLE + tileAnimationState.value = TileAnimationState.IDLE + } + + fun onDialogDrawingStart() { + tileAnimationState.value = TileAnimationState.DIALOG_LAUNCH + } + + fun onDialogDrawingEnd() { + tileAnimationState.value = TileAnimationState.IDLE + } + + fun onActivityLaunchTransitionStart() { + tileAnimationState.value = TileAnimationState.ACTIVITY_LAUNCH + } + + fun onActivityLaunchTransitionEnd() { + tileAnimationState.value = TileAnimationState.IDLE + } + + fun setTileInteractionState(actionState: TileInteractionState) { + tileInteractionState.value = actionState + } + + fun createStateAwareExpandable(baseExpandable: Expandable): Expandable = + baseExpandable.withStateAwareness( + onDialogDrawingStart = ::onDialogDrawingStart, + onDialogDrawingEnd = ::onDialogDrawingEnd, + onActivityLaunchTransitionStart = ::onActivityLaunchTransitionStart, + onActivityLaunchTransitionEnd = ::onActivityLaunchTransitionEnd, + ) + + /** Models the state of toggle haptics to play */ + enum class TileHapticsState { + TOGGLE_ON, + TOGGLE_OFF, + LONG_PRESS, + FAILED_LONGPRESS, + NO_HAPTICS, + } + + /** Models the interaction that took place on the tile */ + enum class TileInteractionState { + IDLE, + CLICKED, + LONG_CLICKED, + } + + /** Models the animation state of dialogs and activity launches from a tile */ + enum class TileAnimationState { + IDLE, + DIALOG_LAUNCH, + ACTIVITY_LAUNCH, + } + + @AssistedFactory + interface Factory { + fun create(tileViewModel: TileViewModel): TileHapticsViewModel + } +} + +class TileHapticsViewModelFactoryProvider +@Inject +constructor(private val tileHapticsViewModelFactory: TileHapticsViewModel.Factory) { + fun getHapticsViewModelFactory(): TileHapticsViewModel.Factory? = + if (Flags.msdlFeedback()) { + tileHapticsViewModelFactory + } else { + null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt index e09e1987698d..2e792497906e 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt @@ -178,7 +178,11 @@ constructor( State.RUNNING_BACKWARDS_FROM_UP -> { callback?.onEffectFinishedReversing() setState(getStateForClick()) - logEvent(qsTile?.tileSpec, state, "click action triggered") + logEvent( + qsTile?.tileSpec, + state, + "click action triggered from handleAnimationComplete", + ) qsTile?.click(expandable) } State.RUNNING_BACKWARDS_FROM_CANCEL -> { @@ -206,7 +210,7 @@ constructor( if (keyguardStateController.isPrimaryBouncerShowing || !isStateClickable) return false setState(getStateForClick()) - logEvent(qsTile?.tileSpec, state, "click action triggered") + logEvent(qsTile?.tileSpec, state, "click action triggered from onTileClick") qsTile?.click(expandable) return true } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt index cc77f68a801e..f6d7e1562291 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt @@ -26,7 +26,7 @@ import com.google.android.msdl.domain.MSDLPlayer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * A plugin added to a manager of a [android.widget.SeekBar] that adds dynamic haptic feedback. diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt index 14cf4110272f..eef71072f178 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderStateTracker.kt @@ -22,7 +22,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Slider tracker attached to a slider. diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt index 002b5aa743ba..af08f40d3dfc 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderTracker.kt @@ -19,7 +19,7 @@ package com.android.systemui.haptics.slider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.cancel -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Tracker component for a slider. diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt index 48f5cb6dc219..b014a33ca5bc 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt @@ -16,7 +16,9 @@ package com.android.systemui.inputdevice.tutorial +import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.domain.interactor.ConnectionState +import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen as KeyboardTouchpadTutorialScreen import com.android.systemui.log.ConstantStringsLogger import com.android.systemui.log.ConstantStringsLoggerImpl @@ -41,7 +43,7 @@ constructor(@InputDeviceTutorialLog private val buffer: LogBuffer) : str1 = screen.toString() str2 = context.string }, - { "Emitting new screen $str1 in $str2" } + { "Emitting new screen $str1 in $str2" }, ) } @@ -58,7 +60,7 @@ constructor(@InputDeviceTutorialLog private val buffer: LogBuffer) : TAG, LogLevel.WARNING, { str1 = nextScreen.toString() }, - { "next screen should be $str1 but required hardware is missing" } + { "next screen should be $str1 but required hardware is missing" }, ) } @@ -72,20 +74,20 @@ constructor(@InputDeviceTutorialLog private val buffer: LogBuffer) : bool1 = connectionState.touchpadConnected bool2 = connectionState.keyboardConnected }, - { "Received connection state: touchpad connected: $bool1 keyboard connected: $bool2" } + { "Received connection state: touchpad connected: $bool1 keyboard connected: $bool2" }, ) } fun logMovingBetweenScreens( previousScreen: KeyboardTouchpadTutorialScreen?, - currentScreen: KeyboardTouchpadTutorialScreen + currentScreen: KeyboardTouchpadTutorialScreen, ) { logInfo( { str1 = previousScreen?.toString() ?: "NO_SCREEN" str2 = currentScreen.toString() }, - { "Moving from $str1 screen to $str2 screen" } + { "Moving from $str1 screen to $str2 screen" }, ) } @@ -93,9 +95,17 @@ constructor(@InputDeviceTutorialLog private val buffer: LogBuffer) : logInfo({ str1 = previousScreen.toString() }, { "Going back to $str1 screen" }) } + fun logDeviceFirstConnection(deviceType: DeviceType) { + logInfo({ str1 = deviceType.toString() }, { "$str1 has connected for the first time" }) + } + + fun logTutorialLaunched(tutorialType: TutorialType) { + logInfo({ str1 = tutorialType.toString() }, { "Launching $str1 tutorial" }) + } + private inline fun logInfo( messageInitializer: MessageInitializer, - noinline messagePrinter: MessagePrinter + noinline messagePrinter: MessagePrinter, ) { buffer.log(TAG, LogLevel.INFO, messageInitializer, messagePrinter) } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt index cfc913fbc89b..3b4d00db1a74 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt @@ -18,6 +18,7 @@ package com.android.systemui.inputdevice.tutorial.domain.interactor import android.os.SystemProperties import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD @@ -47,12 +48,13 @@ class TutorialSchedulerInteractor constructor( keyboardRepository: KeyboardRepository, touchpadRepository: TouchpadRepository, - private val repo: TutorialSchedulerRepository + private val repo: TutorialSchedulerRepository, + private val logger: InputDeviceTutorialLogger, ) { private val isAnyDeviceConnected = mapOf( KEYBOARD to keyboardRepository.isAnyKeyboardConnected, - TOUCHPAD to touchpadRepository.isAnyTouchpadConnected + TOUCHPAD to touchpadRepository.isAnyTouchpadConnected, ) private val touchpadScheduleFlow = flow { @@ -71,10 +73,14 @@ constructor( private suspend fun schedule(deviceType: DeviceType) { if (!repo.wasEverConnected(deviceType)) { + logger.d("Waiting for $deviceType to connect") waitForDeviceConnection(deviceType) + logger.logDeviceFirstConnection(deviceType) repo.updateFirstConnectionTime(deviceType, Instant.now()) } - delay(remainingTime(start = repo.firstConnectionTime(deviceType)!!)) + val remainingTime = remainingTime(start = repo.firstConnectionTime(deviceType)!!) + logger.d("Tutorial is scheduled in ${remainingTime.inWholeSeconds} seconds") + delay(remainingTime) waitForDeviceConnection(deviceType) } @@ -92,6 +98,7 @@ constructor( if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH) repo.updateLaunchTime(TOUCHPAD, Instant.now()) + logger.logTutorialLaunched(tutorialType) tutorialType } @@ -119,7 +126,7 @@ constructor( Duration.ofSeconds( SystemProperties.getLong( "persist.peripheral_tutorial_delay_sec", - DEFAULT_LAUNCH_DELAY_SEC + DEFAULT_LAUNCH_DELAY_SEC, ) ) } @@ -128,6 +135,6 @@ constructor( KEYBOARD, TOUCHPAD, BOTH, - NONE + NONE, } } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt index f2afaee1870b..3b26f2ff9deb 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt @@ -38,9 +38,10 @@ import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutoria import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD import com.android.systemui.res.R +import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** When the scheduler is due, show a notification to launch tutorial */ @SysUISingleton @@ -51,6 +52,7 @@ constructor( @Application private val context: Context, private val tutorialSchedulerInteractor: TutorialSchedulerInteractor, private val notificationManager: NotificationManager, + private val userTracker: UserTracker, ) { fun start() { backgroundScope.launch { @@ -85,7 +87,7 @@ constructor( .addExtras(extras) .build() - notificationManager.notify(TAG, NOTIFICATION_ID, notification) + notificationManager.notifyAsUser(TAG, NOTIFICATION_ID, notification, userTracker.userHandle) } private fun createNotificationChannel() { diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt index 4142be3f9358..058e5874ae7c 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt @@ -52,7 +52,7 @@ fun ActionKeyTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) Modifier.fillMaxSize() .onKeyEvent { keyEvent: KeyEvent -> if (keyEvent.key == Key.MetaLeft && keyEvent.type == KeyEventType.KeyUp) { - actionState = Finished + actionState = Finished(R.raw.action_key_success) } true } @@ -80,11 +80,7 @@ private fun buildScreenConfig() = titleSuccessResId = R.string.tutorial_action_key_success_title, bodySuccessResId = R.string.tutorial_action_key_success_body, ), - animations = - TutorialScreenConfig.Animations( - educationResId = R.raw.action_key_edu, - successResId = R.raw.action_key_success, - ), + animations = TutorialScreenConfig.Animations(educationResId = R.raw.action_key_edu), ) @Composable diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt index 8e01e3765c32..3d2baee9b936 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt @@ -16,6 +16,7 @@ package com.android.systemui.inputdevice.tutorial.ui.composable +import androidx.annotation.RawRes import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -48,7 +49,7 @@ sealed interface TutorialActionState { val endMarker: String? = null, ) : TutorialActionState - data object Finished : TutorialActionState + data class Finished(@RawRes val successAnimation: Int) : TutorialActionState } @Composable @@ -68,11 +69,11 @@ fun ActionTutorialContent( Row(modifier = Modifier.fillMaxWidth().weight(1f)) { TutorialDescription( titleTextId = - if (actionState == Finished) config.strings.titleSuccessResId + if (actionState is Finished) config.strings.titleSuccessResId else config.strings.titleResId, titleColor = config.colors.title, bodyTextId = - if (actionState == Finished) config.strings.bodySuccessResId + if (actionState is Finished) config.strings.bodySuccessResId else config.strings.bodyResId, modifier = Modifier.weight(1f), ) @@ -83,7 +84,7 @@ fun ActionTutorialContent( modifier = Modifier.weight(1f).padding(top = 8.dp), ) } - AnimatedVisibility(visible = actionState == Finished, enter = fadeIn()) { + AnimatedVisibility(visible = actionState is Finished, enter = fadeIn()) { DoneButton(onDoneButtonClicked = onDoneButtonClicked) } } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt index ef375a868cef..720c01fc7056 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialAnimation.kt @@ -77,7 +77,9 @@ fun TutorialAnimation( config.colors.animationColors, ) Finished::class -> - SuccessAnimation(config.animations.successResId, config.colors.animationColors) + // Below cast is safe as Finished state is the last state and afterwards we can + // only leave the screen so this composable would be no longer displayed + SuccessAnimation(actionState as Finished, config.colors.animationColors) } } } @@ -100,10 +102,11 @@ private fun EducationAnimation( @Composable private fun SuccessAnimation( - @RawRes successAnimationId: Int, + finishedState: Finished, animationProperties: LottieDynamicProperties, ) { - val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(successAnimationId)) + val composition by + rememberLottieComposition(LottieCompositionSpec.RawRes(finishedState.successAnimation)) val progress by animateLottieCompositionAsState(composition, iterations = 1) LottieAnimation( composition = composition, diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt index 55e5f2d79e60..60dfed3a67a4 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt @@ -24,13 +24,13 @@ import com.airbnb.lottie.compose.LottieDynamicProperties data class TutorialScreenConfig( val colors: Colors, val strings: Strings, - val animations: Animations + val animations: Animations, ) { data class Colors( val background: Color, val title: Color, - val animationColors: LottieDynamicProperties + val animationColors: LottieDynamicProperties, ) data class Strings( @@ -40,8 +40,5 @@ data class TutorialScreenConfig( @StringRes val bodySuccessResId: Int, ) - data class Animations( - @RawRes val educationResId: Int, - @RawRes val successResId: Int, - ) + data class Animations(@RawRes val educationResId: Int) } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt index 29febd32e925..fa494150cbbf 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt @@ -40,7 +40,7 @@ import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTUR import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE import java.util.Optional import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt index 5cf19677a98e..896bdc068154 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.runningFold -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class KeyboardTouchpadTutorialViewModel( private val gesturesInteractor: Optional<TouchpadGesturesInteractor>, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt index 1f421fae2517..2501d8a33865 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt @@ -25,7 +25,7 @@ import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogConte import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private fun defaultCreateDialog(context: Context): (Int, Int) -> KeyboardBacklightDialog { return { currentLevel: Int, maxLevel: Int -> diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt index b859cdc5dee5..c2974a8d5429 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt @@ -29,7 +29,7 @@ import com.android.systemui.surfaceeffects.PaintDrawCallback import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxEffect import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class KeyboardDockingIndicationViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt index 2578b785056d..f0b2b86d67a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt @@ -29,7 +29,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class KeyboardDockingIndicationViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt index 85bd0b0b1968..a08588750f85 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt @@ -68,7 +68,7 @@ constructor( @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource, @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource, private val inputManager: InputManager, - stateRepository: ShortcutHelperStateRepository + stateRepository: ShortcutHelperStateRepository, ) { private val sources = @@ -76,27 +76,27 @@ constructor( InternalGroupsSource( source = systemShortcutsSource, isTrusted = true, - typeProvider = { System } + typeProvider = { System }, ), InternalGroupsSource( source = multitaskingShortcutsSource, isTrusted = true, - typeProvider = { MultiTasking } + typeProvider = { MultiTasking }, ), InternalGroupsSource( source = appCategoriesShortcutsSource, isTrusted = true, - typeProvider = { AppCategories } + typeProvider = { AppCategories }, ), InternalGroupsSource( source = inputShortcutsSource, isTrusted = false, - typeProvider = { InputMethodEditor } + typeProvider = { InputMethodEditor }, ), InternalGroupsSource( source = currentAppShortcutsSource, isTrusted = false, - typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) } + typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) }, ), ) @@ -179,7 +179,7 @@ constructor( shortcutGroup.items, keepIcons, supportedKeyCodes, - ) + ), ) } .filter { it.shortcuts.isNotEmpty() } @@ -214,13 +214,13 @@ constructor( return Shortcut( label = shortcutInfo.label!!.toString(), icon = toShortcutIcon(keepIcon, shortcutInfo), - commands = listOf(shortcutCommand) + commands = listOf(shortcutCommand), ) } private fun toShortcutIcon( keepIcon: Boolean, - shortcutInfo: KeyboardShortcutInfo + shortcutInfo: KeyboardShortcutInfo, ): ShortcutIcon? { if (!keepIcon) { return null @@ -236,13 +236,13 @@ constructor( private fun toShortcutCommand( keyCharacterMap: KeyCharacterMap, - info: KeyboardShortcutInfo + info: KeyboardShortcutInfo, ): ShortcutCommand? { val keys = mutableListOf<ShortcutKey>() var remainingModifiers = info.modifiers SUPPORTED_MODIFIERS.forEach { supportedModifier -> if ((supportedModifier and remainingModifiers) != 0) { - keys += toShortcutKey(keyCharacterMap, supportedModifier) ?: return null + keys += toShortcutModifierKey(supportedModifier) ?: return null // "Remove" the modifier from the remaining modifiers remainingModifiers = remainingModifiers and supportedModifier.inv() } @@ -262,6 +262,20 @@ constructor( return ShortcutCommand(keys) } + private fun toShortcutModifierKey(modifierMask: Int): ShortcutKey? { + val iconResId = ShortcutHelperKeys.keyIcons[modifierMask] + if (iconResId != null) { + return ShortcutKey.Icon(iconResId) + } + + val modifierLabel = ShortcutHelperKeys.modifierLabels[modifierMask] + if (modifierLabel != null) { + return ShortcutKey.Text(modifierLabel(context)) + } + Log.wtf("TAG", "Couldn't find label or icon for modifier $modifierMask") + return null + } + private fun toShortcutKey( keyCharacterMap: KeyCharacterMap, keyCode: Int, @@ -289,7 +303,7 @@ constructor( private suspend fun fetchSupportedKeyCodes( deviceId: Int, - groupsFromAllSources: List<List<KeyboardShortcutGroup>> + groupsFromAllSources: List<List<KeyboardShortcutGroup>>, ): Set<Int> = withContext(backgroundDispatcher) { val allUsedKeyCodes = @@ -320,7 +334,7 @@ constructor( KeyEvent.META_ALT_ON, KeyEvent.META_SHIFT_ON, KeyEvent.META_SYM_ON, - KeyEvent.META_FUNCTION_ON + KeyEvent.META_FUNCTION_ON, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt index 8db16fa9a06e..288efa275219 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt @@ -124,6 +124,17 @@ object ShortcutHelperKeys { KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank, ) + val modifierLabels = + mapOf<Int, (Context) -> String>( + // Modifiers + META_META_ON to { "Meta" }, + META_CTRL_ON to { "Ctrl" }, + META_ALT_ON to { "Alt" }, + META_SHIFT_ON to { "Shift" }, + META_SYM_ON to { "Sym" }, + META_FUNCTION_ON to { "Fn" }, + ) + val specialKeyLabels = mapOf<Int, (Context) -> String>( KEYCODE_HOME to { context -> context.getString(R.string.keyboard_key_home) }, @@ -317,7 +328,7 @@ object ShortcutHelperKeys { { context -> context.getString( R.string.keyboard_key_numpad_template, - context.getString(R.string.keyboard_key_enter) + context.getString(R.string.keyboard_key_enter), ) }, KEYCODE_NUMPAD_EQUALS to @@ -343,13 +354,5 @@ object ShortcutHelperKeys { KEYCODE_CTRL_RIGHT to { "Ctrl" }, KEYCODE_SHIFT_LEFT to { "Shift" }, KEYCODE_SHIFT_RIGHT to { "Shift" }, - - // Modifiers - META_META_ON to { "Meta" }, - META_CTRL_ON to { "Ctrl" }, - META_ALT_ON to { "Alt" }, - META_SHIFT_ON to { "Shift" }, - META_SYM_ON to { "Sym" }, - META_FUNCTION_ON to { "Fn" }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt index 82df95d482c2..aa6b61b6215d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt @@ -36,7 +36,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt index 299628ee19f6..cea3b6442feb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt @@ -27,7 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class ShortcutHelperStateInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt index 901eafa29418..5cade686ae09 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt @@ -777,7 +777,7 @@ private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick ) { Row(verticalAlignment = Alignment.CenterVertically) { Text( - "Keyboard Settings", + stringResource(id = R.string.shortcut_helper_keyboard_settings_buttons_label), color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 16.sp, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt index e49ce6062be3..f64d59a26893 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt @@ -61,7 +61,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import com.android.compose.modifiers.thenIf -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * A selectable surface with no default focus/hover indications. diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt index 78c4e77cad74..3aca9bdb7ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt @@ -23,7 +23,7 @@ import com.android.systemui.keyboard.stickykeys.StickyKeysLogger import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class StickyKeysIndicatorCoordinator diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt index b9e513586298..b3027edb5f0a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt @@ -31,7 +31,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Releases cached resources on allocated by keyguard. @@ -52,7 +52,7 @@ constructor( override fun start() { Log.d(LOG_TAG, "Resource trimmer registered.") - applicationScope.launch(bgDispatcher) { + applicationScope.launch(context = bgDispatcher) { keyguardTransitionInteractor .isFinishedIn(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) .filter { isOnGone -> isOnGone } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt index 72747f68bbbd..c1384e7629a9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncer.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt index e6bab4c7edcf..8be11a4691be 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt @@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Manages and provides access to the current "selections" of keyguard quick affordances, answering diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt index a145214e5161..6c1bdad98251 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt @@ -44,7 +44,7 @@ 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 com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @SysUISingleton @@ -103,7 +103,7 @@ constructor( override fun onTriggered( expandable: Expandable? ): KeyguardQuickAffordanceConfig.OnTriggeredResult { - coroutineScope.launch(backgroundDispatcher) { + coroutineScope.launch(context = backgroundDispatcher) { val newRingerMode: Int val currentRingerMode = audioManager.ringerModeInternal if (currentRingerMode == AudioManager.RINGER_MODE_SILENT) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt index 7dbe945c1c6f..8e1ba4137947 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import javax.inject.Inject /** @@ -71,7 +71,7 @@ class MuteQuickAffordanceCoreStartable @Inject constructor( } private fun updateLastNonSilentRingerMode(lastRingerMode: Int) { - coroutineScope.launch(backgroundDispatcher) { + coroutineScope.launch(context = backgroundDispatcher) { if (AudioManager.RINGER_MODE_SILENT != lastRingerMode) { userFileManager.getSharedPreferences( MuteQuickAffordanceConfig.MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 9e99a879be41..d3c17ccd2d18 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -64,7 +64,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Defines interface for classes that encapsulate application state for the keyguard. */ interface KeyguardRepository { @@ -269,6 +269,8 @@ interface KeyguardRepository { /** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */ val shortcutAbsoluteTop: StateFlow<Float> + val notificationStackAbsoluteBottom: StateFlow<Float> + /** * Returns `true` if the keyguard is showing; `false` otherwise. * @@ -339,6 +341,12 @@ interface KeyguardRepository { fun isShowKeyguardWhenReenabled(): Boolean fun setShortcutAbsoluteTop(top: Float) + + /** + * Set bottom of notifications from notification stack, and Magic Portrait will layout base on + * this value + */ + fun setNotificationStackAbsoluteBottom(bottom: Float) } /** Encapsulates application state for the keyguard. */ @@ -635,6 +643,9 @@ constructor( private val _shortcutAbsoluteTop = MutableStateFlow(0F) override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow() + private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) + override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow() + init { val callback = object : KeyguardStateController.Callback { @@ -717,6 +728,10 @@ constructor( _shortcutAbsoluteTop.value = top } + override fun setNotificationStackAbsoluteBottom(bottom: Float) { + _notificationStackAbsoluteBottom.value = bottom + } + private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel { return when (state) { DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index b7d0d453779f..3a5614fbc430 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -25,6 +25,7 @@ import android.os.Trace import android.util.Log import com.android.app.animation.Interpolators import com.android.app.tracing.coroutines.withContextTraced as withContext +import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.model.KeyguardState @@ -77,6 +78,8 @@ interface KeyguardTransitionRepository { /** The [TransitionInfo] of the most recent call to [startTransition]. */ val currentTransitionInfoInternal: StateFlow<TransitionInfo> + /** The [TransitionInfo] of the most recent call to [startTransition]. */ + val currentTransitionInfo: TransitionInfo /** * Interactors that require information about changes between [KeyguardState]s will call this to @@ -132,7 +135,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR private var lastStep: TransitionStep = TransitionStep() private var lastAnimator: ValueAnimator? = null - private val _currentTransitionMutex = Mutex() + private val withContextMutex = Mutex() private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = MutableStateFlow( TransitionInfo( @@ -144,6 +147,16 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR ) override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow() + @Volatile + override var currentTransitionInfo: TransitionInfo = + TransitionInfo( + ownerName = "", + from = KeyguardState.OFF, + to = KeyguardState.OFF, + animator = null, + ) + private set + /* * When manual control of the transition is requested, a unique [UUID] is used as the handle * to permit calls to [updateTransition] @@ -163,13 +176,17 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR } override suspend fun startTransition(info: TransitionInfo): UUID? { - _currentTransitionInfo.value = info + if (transitionRaceCondition()) { + currentTransitionInfo = info + } else { + _currentTransitionInfo.value = info + } Log.d(TAG, "(Internal) Setting current transition info: $info") // There is no fairness guarantee with 'withContext', which means that transitions could // be processed out of order. Use a Mutex to guarantee ordering. [updateTransition] // requires the same lock - _currentTransitionMutex.lock() + withContextMutex.lock() // Only used in a test environment if (forceDelayForRaceConditionTest) { delay(50L) @@ -177,7 +194,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR // Animators must be started on the main thread. return withContext("$TAG#startTransition", mainDispatcher) { - _currentTransitionMutex.unlock() + withContextMutex.unlock() if (lastStep.from == info.from && lastStep.to == info.to) { Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") return@withContext null @@ -265,9 +282,9 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR // There is no fairness guarantee with 'withContext', which means that transitions could // be processed out of order. Use a Mutex to guarantee ordering. [startTransition] // requires the same lock - _currentTransitionMutex.lock() + withContextMutex.lock() withContext("$TAG#updateTransition", mainDispatcher) { - _currentTransitionMutex.unlock() + withContextMutex.unlock() updateTransitionInternal(transitionId, value, state) } @@ -302,13 +319,23 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR // Tests runs on testDispatcher, which is not the main thread, causing the animator thread // check to fail if (testSetup) { - _currentTransitionInfo.value = - TransitionInfo( - ownerName = ownerName, - from = KeyguardState.OFF, - to = to, - animator = null, - ) + if (transitionRaceCondition()) { + currentTransitionInfo = + TransitionInfo( + ownerName = ownerName, + from = KeyguardState.OFF, + to = to, + animator = null, + ) + } else { + _currentTransitionInfo.value = + TransitionInfo( + ownerName = ownerName, + from = KeyguardState.OFF, + to = to, + animator = null, + ) + } emitTransition( TransitionStep( KeyguardState.OFF, 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 e4b0f6ea69a9..c755c4b02feb 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 @@ -46,7 +46,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromAlternateBouncerTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 0c2d5778079b..6ac0a3f8443f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -41,7 +41,7 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromDozingTransitionInteractor 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 7b757657b1d9..1e672e23970b 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 @@ -48,7 +48,7 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton 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 a6f0db5a86aa..3565b612a3c9 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 @@ -48,7 +48,7 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @OptIn(FlowPreview::class) 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 a4a215f6eeef..bc2ed71f156c 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 @@ -38,7 +38,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromGoneTransitionInteractor 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 e84db062a5f5..8b75545fddc9 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 @@ -40,6 +40,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.sample import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds @@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromLockscreenTransitionInteractor @@ -132,11 +133,10 @@ constructor( scope.launch("$TAG#listenForLockscreenToDreaming") { keyguardInteractor.isAbleToDream .filterRelevantKeyguardState() - .sampleCombine( - internalTransitionInteractor.currentTransitionInfoInternal, - transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), - ) - .collect { (isAbleToDream, transitionInfo, isOnLockscreen) -> + .sample(transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), ::Pair) + .collect { (isAbleToDream, isOnLockscreen) -> + val transitionInfo = + internalTransitionInteractor.currentTransitionInfoInternal() val isTransitionInterruptible = transitionInfo.to == KeyguardState.LOCKSCREEN && !invalidFromStates.contains(transitionInfo.from) @@ -179,7 +179,6 @@ constructor( shadeRepository.legacyShadeExpansion .sampleCombine( transitionInteractor.startedKeyguardTransitionStep, - internalTransitionInteractor.currentTransitionInfoInternal, keyguardInteractor.statusBarState, keyguardInteractor.isKeyguardDismissible, keyguardInteractor.isKeyguardOccluded, @@ -188,11 +187,12 @@ constructor( ( shadeExpansion, startedStep, - currentTransitionInfo, statusBarState, isKeyguardUnlocked, isKeyguardOccluded) -> val id = transitionId + val currentTransitionInfo = + internalTransitionInteractor.currentTransitionInfoInternal() if (id != null) { if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) { // An existing `id` means a transition is started, and calls to @@ -358,7 +358,7 @@ constructor( if (!communalSettingsInteractor.isCommunalFlagEnabled()) { return } - scope.launch(mainDispatcher) { + scope.launch(context = mainDispatcher) { glanceableHubTransitions.listenForGlanceableHubTransition( transitionOwnerName = TAG, fromState = KeyguardState.LOCKSCREEN, 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 840bc0fb5f99..a9011513d797 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 @@ -38,7 +38,7 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromOccludedTransitionInteractor 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 0ecf7816e9b7..402c152070a2 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 @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class FromPrimaryBouncerTransitionInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt index 2cc6afa2f407..05078346399a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt @@ -17,13 +17,13 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.FloatRange +import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionState import java.util.UUID import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow /** * This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes @@ -32,9 +32,7 @@ import kotlinx.coroutines.flow.StateFlow @SysUISingleton class InternalKeyguardTransitionInteractor @Inject -constructor( - private val repository: KeyguardTransitionRepository, -) { +constructor(private val repository: KeyguardTransitionRepository) { /** * The [TransitionInfo] of the most recent call to @@ -58,14 +56,19 @@ constructor( * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition * which will subsequently cancel GONE -> AOD. */ - internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> = - repository.currentTransitionInfoInternal + internal fun currentTransitionInfoInternal(): TransitionInfo { + return if (transitionRaceCondition()) { + repository.currentTransitionInfo + } else { + repository.currentTransitionInfoInternal.value + } + } suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info) suspend fun updateTransition( transitionId: UUID, @FloatRange(from = 0.0, to = 1.0) value: Float, - state: TransitionState + state: TransitionState, ) = repository.updateTransition(transitionId, value, state) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt index 6932eb51e141..6385b3cffbd4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class KeyguardBlueprintInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt index 9b9bdd1bde9b..2d7da38535b1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Encapsulates business logic for requesting the keyguard to dismiss/finish/done. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt index c19bbbce3b4b..4793d95b121c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.domain.interactor import android.util.Log +import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -51,7 +52,13 @@ constructor( fun startDismissKeyguardTransition(reason: String = "") { if (SceneContainerFlag.isEnabled) return Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)") - when (val startedState = repository.currentTransitionInfoInternal.value.to) { + val startedState = + if (transitionRaceCondition()) { + repository.currentTransitionInfo.to + } else { + repository.currentTransitionInfoInternal.value.to + } + when (startedState) { LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard() PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer() ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer() @@ -61,7 +68,7 @@ constructor( KeyguardState.GONE -> Log.i( TAG, - "Already transitioning to GONE; ignoring startDismissKeyguardTransition." + "Already transitioning to GONE; ignoring startDismissKeyguardTransition.", ) else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt index ad1a32e70a5b..631e44aca26d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt @@ -22,7 +22,7 @@ import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not @@ -74,11 +74,9 @@ constructor( .onEach { SceneContainerFlag.assertInLegacyMode() } // Whenever the keyguard is disabled... .filter { enabled -> !enabled } - .sampleCombine( - internalTransitionInteractor.currentTransitionInfoInternal, - biometricSettingsRepository.isCurrentUserInLockdown, - ) - .map { (_, transitionInfo, inLockdown) -> + .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair) + .map { (_, inLockdown) -> + val transitionInfo = internalTransitionInteractor.currentTransitionInfoInternal() // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case, // we want to remember that and re-show it when keyguard is enabled again. transitionInfo.to != KeyguardState.GONE && !inLockdown @@ -93,11 +91,10 @@ constructor( if (!SceneContainerFlag.isEnabled) { repository.isKeyguardEnabled .filter { enabled -> !enabled } - .sampleCombine( - biometricSettingsRepository.isCurrentUserInLockdown, - internalTransitionInteractor.currentTransitionInfoInternal, - ) - .collect { (_, inLockdown, currentTransitionInfo) -> + .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair) + .collect { (_, inLockdown) -> + val currentTransitionInfo = + internalTransitionInteractor.currentTransitionInfoInternal() if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) { keyguardDismissTransitionInteractor.startDismissKeyguardTransition( "keyguard disabled" diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 0d5ad547f15f..b24ca1a8d345 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -50,7 +50,6 @@ 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.shade.data.repository.ShadeRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.systemui.util.kotlin.sample import javax.inject.Inject @@ -93,7 +92,6 @@ constructor( private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>, private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>, private val fromOccludedTransitionInteractor: Provider<FromOccludedTransitionInteractor>, - sharedNotificationContainerInteractor: Provider<SharedNotificationContainerInteractor>, @Application applicationScope: CoroutineScope, ) { // TODO(b/296118689): move to a repository @@ -104,15 +102,16 @@ constructor( SceneContainerFlag.assertInLegacyMode() combineTransform( _notificationPlaceholderBounds, - sharedNotificationContainerInteractor.get().configurationBasedDimensions, keyguardTransitionInteractor.isInTransition( edge = Edge.create(from = LOCKSCREEN, to = AOD) ), - ) { bounds, cfg, isTransitioningToAod -> + shadeRepository.isShadeLayoutWide, + configurationInteractor.dimensionPixelSize(R.dimen.keyguard_split_shade_top_margin), + ) { bounds, isTransitioningToAod, useSplitShade, keyguardSplitShadeTopMargin -> if (isTransitioningToAod) { // Keep bounds stable during this transition, to prevent cases like smartspace // popping in and adjusting the bounds. A prime example would be media playing, - // which then updates smartspace on transition to AOD + // which then updates smartspace on transition to AOD. return@combineTransform } @@ -120,8 +119,8 @@ constructor( // legacy placement behavior within notifications for splitshade. emit( if (MigrateClocksToBlueprint.isEnabled) { - if (cfg.useSplitShade) { - bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin) + if (useSplitShade) { + bounds.copy(bottom = bounds.bottom - keyguardSplitShadeTopMargin) } else { bounds } @@ -546,6 +545,10 @@ constructor( repository.isKeyguardGoingAway.value = isGoingAway } + fun setNotificationStackAbsoluteBottom(bottom: Float) { + repository.setNotificationStackAbsoluteBottom(bottom) + } + companion object { private const val TAG = "KeyguardInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt index 7f1e881c0863..278a98f8b157 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt @@ -80,7 +80,7 @@ constructor( // *_BOUNCER -> LOCKSCREEN. return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered && KeyguardState.deviceIsAsleepInState( - internalTransitionInteractor.currentTransitionInfoInternal.value.to + internalTransitionInteractor.currentTransitionInfoInternal().to ) } @@ -100,13 +100,13 @@ constructor( scene = Scenes.Gone, stateWithoutSceneContainer = KeyguardState.GONE, ), - ::Pair + ::Pair, ) .map { (wakefulness, isOnGone) -> wakefulness.powerButtonLaunchGestureTriggered && !isOnGone }, // Emit false once that activity goes away. - isShowWhenLockedActivityOnTop.filter { !it }.map { false } + isShowWhenLockedActivityOnTop.filter { !it }.map { false }, ) .stateIn(applicationScope, SharingStarted.Eagerly, false) @@ -134,7 +134,7 @@ constructor( */ fun setWmNotifiedShowWhenLockedActivityOnTop( showWhenLockedActivityOnTop: Boolean, - taskInfo: RunningTaskInfo? = null + taskInfo: RunningTaskInfo? = null, ) { repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt index 6fe4ff5122d0..373f5a1aef23 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardStateCallbackInteractor.kt @@ -34,7 +34,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt index 505c749d9e44..b2031d300a82 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Business logic for use-cases related to top-level touch handling in the lock screen. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt index cf9d60fff2c6..0dae17c594c8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt @@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNoti import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private val TAG = KeyguardTransitionAuditLogger::class.simpleName!! diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt index 89f636d4a270..b986d56e9a82 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Handles initialization of the KeyguardTransitionRepository on boot. */ @SysUISingleton @@ -61,7 +61,7 @@ constructor( fun start() { scope.launch { - if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) { + if (internalTransitionInteractor.currentTransitionInfoInternal().from != OFF) { Log.e( "KeyguardTransitionInteractor", "showLockscreenOnBoot emitted, but we've already " + 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 a0000f3c66fd..b815f1988e7e 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 @@ -59,7 +59,7 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates business-logic related to the keyguard transitions. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt index 9b8d9ea11edf..9c98a96ea908 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt @@ -52,7 +52,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt index 34173a9f44ea..6d9c6a666f56 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class LightRevealScrimInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt index 43aab355d6d1..73e80ff1d02b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index ba12e9356ed7..abd7f90bbf22 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Each TransitionInteractor is responsible for determining under which conditions to notify @@ -71,14 +71,14 @@ sealed class TransitionInteractor( ownerReason: String = "", ): UUID? { toState.checkValidState() - if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) { + if (fromState != internalTransitionInteractor.currentTransitionInfoInternal().to) { Log.e( name, "Ignoring startTransition: This interactor asked to transition from " + "$fromState -> $toState, but we last transitioned to " + - "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" + + "${internalTransitionInteractor.currentTransitionInfoInternal().to}, not" + " $fromState. This should never happen - check currentTransitionInfoInternal" + - " or use filterRelevantKeyguardState before starting transitions." + " or use filterRelevantKeyguardState before starting transitions.", ) return null } @@ -149,7 +149,7 @@ sealed class TransitionInteractor( if (keyguardInteractor.isKeyguardDismissible.value) { startTransitionTo( KeyguardState.GONE, - ownerReason = "Power button gesture while keyguard is dismissible" + ownerReason = "Power button gesture while keyguard is dismissible", ) return true @@ -159,7 +159,7 @@ sealed class TransitionInteractor( // should transition to GONE. startTransitionTo( KeyguardState.GONE, - ownerReason = "Power button gesture on dismissable keyguard" + ownerReason = "Power button gesture on dismissable keyguard", ) return true @@ -190,16 +190,13 @@ sealed class TransitionInteractor( startTransitionTo( toState = keyguardInteractor.asleepKeyguardState.value, modeOnCanceled = modeOnCanceled, - ownerReason = "Sleep transition triggered" + ownerReason = "Sleep transition triggered", ) } } /** This signal may come in before the occlusion signal, and can provide a custom transition */ - fun listenForTransitionToCamera( - scope: CoroutineScope, - keyguardInteractor: KeyguardInteractor, - ) { + fun listenForTransitionToCamera(scope: CoroutineScope, keyguardInteractor: KeyguardInteractor) { if (!KeyguardWmStateRefactor.isEnabled) { scope.launch { keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect { @@ -223,7 +220,7 @@ sealed class TransitionInteractor( * [startedKeyguardState] as it does not wait for the emission of the first STARTED step. */ fun inOrTransitioningToRelevantKeyguardState(): Boolean { - return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState + return internalTransitionInteractor.currentTransitionInfoInternal().to == fromState } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt index 73248bbec6ca..69a870ed3267 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TrustInteractor.kt @@ -22,7 +22,7 @@ import com.android.systemui.keyguard.data.repository.TrustRepository import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates any state relevant to trust agents and trust grants. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index a09cd7c12d42..a1f606740cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.compose.animation.scene.ObservableTransitionState +import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -65,7 +66,7 @@ constructor( combine( transitionInteractor.isFinishedIn( scene = Scenes.Gone, - stateWithoutSceneContainer = KeyguardState.GONE + stateWithoutSceneContainer = KeyguardState.GONE, ), wakeToGoneInteractor.canWakeDirectlyToGone, ) { isOnGone, canWakeDirectlyToGone -> @@ -197,11 +198,11 @@ constructor( combine( transitionInteractor.isInTransition( edge = Edge.create(to = Scenes.Gone), - edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE) + edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE), ), transitionInteractor.isFinishedIn( scene = Scenes.Gone, - stateWithoutSceneContainer = KeyguardState.GONE + stateWithoutSceneContainer = KeyguardState.GONE, ), surfaceBehindInteractor.isAnimatingSurface, notificationLaunchAnimationInteractor.isLaunchAnimationRunning, @@ -231,7 +232,7 @@ constructor( combine( transitionInteractor.currentKeyguardState, wakeToGoneInteractor.canWakeDirectlyToGone, - ::Pair + ::Pair, ) .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple) .map { (currentState, canWakeDirectlyToGone, startedWithPrev) -> @@ -242,7 +243,12 @@ constructor( startedFromStep.transitionState == TransitionState.CANCELED && startedFromStep.from == KeyguardState.GONE - val transitionInfo = transitionRepository.currentTransitionInfoInternal.value + val transitionInfo = + if (transitionRaceCondition()) { + transitionRepository.currentTransitionInfo + } else { + transitionRepository.currentTransitionInfoInternal.value + } val wakingDirectlyToGone = deviceIsAsleepInState(transitionInfo.from) && transitionInfo.to == KeyguardState.GONE diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt index f3bb8293851f..aa44b6d46289 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt @@ -38,7 +38,7 @@ import java.util.UUID import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * This class listens to scene framework scene transitions and manages keyguard transition framework @@ -106,7 +106,7 @@ constructor( private suspend fun handleIdle( prevTransition: ObservableTransitionState, - idle: ObservableTransitionState.Idle + idle: ObservableTransitionState.Idle, ) { if (currentTransitionId == null) return if (prevTransition !is ObservableTransitionState.Transition) return @@ -133,10 +133,10 @@ constructor( val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, - from = internalTransitionInteractor.currentTransitionInfoInternal.value.to, + from = internalTransitionInteractor.currentTransitionInfoInternal().to, to = state, animator = null, - modeOnCanceled = TransitionModeOnCanceled.REVERSE + modeOnCanceled = TransitionModeOnCanceled.REVERSE, ) currentTransitionId = internalTransitionInteractor.startTransition(newTransition) internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED) @@ -152,8 +152,7 @@ constructor( private suspend fun handleTransition(transition: ObservableTransitionState.Transition) { if (transition.fromContent == Scenes.Lockscreen) { if (currentTransitionId != null) { - val currentToState = - internalTransitionInteractor.currentTransitionInfoInternal.value.to + val currentToState = internalTransitionInteractor.currentTransitionInfoInternal().to if (currentToState == UNDEFINED) { transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from) } @@ -197,21 +196,21 @@ constructor( from = UNDEFINED, to = repository.nextLockscreenTargetState.value, animator = null, - modeOnCanceled = TransitionModeOnCanceled.RESET + modeOnCanceled = TransitionModeOnCanceled.RESET, ) repository.nextLockscreenTargetState.value = DEFAULT_STATE startTransition(newTransition) } private suspend fun startTransitionFromLockscreen() { - val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to + val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to val newTransition = TransitionInfo( ownerName = this::class.java.simpleName, from = currentState, to = UNDEFINED, animator = null, - modeOnCanceled = TransitionModeOnCanceled.RESET + modeOnCanceled = TransitionModeOnCanceled.RESET, ) startTransition(newTransition) } @@ -228,7 +227,7 @@ constructor( internalTransitionInteractor.updateTransition( currentTransitionId!!, progress.coerceIn(0f, 1f), - RUNNING + RUNNING, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt index 8f5a6a1f22e7..824e0228adca 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt @@ -26,7 +26,7 @@ import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** View binder for accessibility actions placeholder on keyguard. */ object AccessibilityActionsViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 6c104a0a2470..be4bc2305922 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -42,7 +42,7 @@ import com.android.systemui.util.kotlin.DisposableHandles import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @ExperimentalCoroutinesApi object DeviceEntryIconViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt index 56a6e9b127f6..9040eac55e7d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.binder import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Binds the [InWindowLauncherUnlockAnimationManager] "view", which manages the lifecycle and state diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 5f76f643b2fe..7292ddaf43a7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object KeyguardClockViewBinder { private val TAG = KeyguardClockViewBinder::class.simpleName!! diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt index 69cb6a92137f..87befc052a15 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissActionBinder.kt @@ -27,7 +27,7 @@ import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Runs actions on keyguard dismissal. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt index b55f813a1f84..a1ca604330df 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardDismissBinder.kt @@ -27,7 +27,7 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.user.domain.interactor.SelectedUserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Handles keyguard dismissal requests. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 21d1db4a5052..46f5c05092eb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -114,7 +114,18 @@ object KeyguardPreviewClockViewBinder { } .invokeOnCompletion { // recover seed color especially for Transit clock - lastClock?.events?.onSeedColorChanged(clockRegistry.seedColor) + lastClock?.apply { + smallClock.run { + events.onThemeChanged( + theme.copy(seedColor = clockRegistry.seedColor) + ) + } + largeClock.run { + events.onThemeChanged( + theme.copy(seedColor = clockRegistry.seedColor) + ) + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index cfd6481234ed..191e08b0de77 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** This is only for a SINGLE Quick affordance */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 89bca0c6a373..f121aabe795a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -95,7 +95,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt index 741cc02ffb6b..97fa3f19a82e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt @@ -26,7 +26,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.shared.Flags.ambientAod import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.wallpapers.ui.viewmodel.WallpaperViewModel -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object LightRevealScrimViewBinder { @JvmStatic diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt index 76edda3c334b..98fe9c3e1c80 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val sfpsProgressBarCommand = "sfps-progress-bar" diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 08d35a72ab12..ab9cffc05667 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -80,6 +80,7 @@ import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessage import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -104,7 +105,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.json.JSONException @@ -653,9 +654,13 @@ constructor( // Note that when [wallpaperColors] is null, isWallpaperDark is true. val isWallpaperDark: Boolean = (colors.colorHints.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0 - clock.events.onSeedColorChanged( - if (isWallpaperDark) lightClockColor else darkClockColor - ) + val theme = + ThemeConfig( + isWallpaperDark, + if (isWallpaperDark) lightClockColor else darkClockColor, + ) + clock.smallClock.events.onThemeChanged(theme) + clock.largeClock.events.onThemeChanged(theme) } // In clock preview, we should have a seed color for clock // before setting clock to clockEventController to avoid updateColor with seedColor == null diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 9355200daec7..f228e26f92f7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -44,7 +44,7 @@ import com.android.systemui.util.kotlin.logD import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class KeyguardRemotePreviewManager @@ -214,7 +214,7 @@ class PreviewLifecycleObserver( this.onDestroy = null val hostToken = rendererToDestroy.hostToken hostToken?.unlinkToDeath(this, 0) - scope.launch(mainDispatcher) { rendererToDestroy.destroy() } + scope.launch(context = mainDispatcher) { rendererToDestroy.destroy() } rendererToDestroy.id } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt index 12bcc7ecbab8..b15cacf077a4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt @@ -35,9 +35,7 @@ import kotlinx.coroutines.flow.Flow @SysUISingleton class DozingToOccludedTransitionViewModel @Inject -constructor( - animationFlow: KeyguardTransitionAnimationFlow, -) : DeviceEntryIconTransition { +constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION, @@ -56,11 +54,7 @@ constructor( var currentAlpha = 0f return transitionAnimation.sharedFlow( duration = 250.milliseconds, - startTime = if (lightRevealMigration()) { - 100.milliseconds // Wait for the light reveal to "hit" the LS elements. - } else { - 0.milliseconds - }, + startTime = 0.milliseconds, onStart = { if (lightRevealMigration()) { currentAlpha = viewState.alpha() @@ -69,7 +63,6 @@ constructor( } }, onStep = { MathUtils.lerp(currentAlpha, 0f, it) }, - onCancel = { 0f }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt index 75b1b0402630..850e943d17eb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class LockscreenContentViewModel @AssistedInject diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt index c5909ed24c50..da96f3fd1f9c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt @@ -59,7 +59,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @ExperimentalCoroutinesApi @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index 7c02f28c0696..881228d597b0 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Keeps snapshot/Compose [State]s up-to-date. diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index a86bfb18fb70..559e1d96c6d8 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Runs the given [block] every time the [View] becomes attached (or immediately after calling this @@ -117,7 +117,7 @@ private fun createLifecycleOwnerAndRun( onCreate() // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the // top-level CoroutineScope used as the lifecycleScope - lifecycleScope.launch(coroutineContext) { block(view) } + lifecycleScope.launch(context = coroutineContext) { block(view) } } } diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt index 508b04eee91a..a81b3e297bf0 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt @@ -22,7 +22,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Returns a remembered view-model of the type [T]. If the returned instance is also an diff --git a/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt index ee58b1465316..21808b691cd0 100644 --- a/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt +++ b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerDebug.kt @@ -27,7 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * A version of [LogcatEchoTracker] that supports fine-grained echoing of log messages to logcat, @@ -108,7 +108,7 @@ constructor( } fun setEchoLevel(type: EchoOverrideType, name: String, level: LogLevel?) { - applicationScope.launch(sequentialBgDispatcher) { + applicationScope.launch(context = sequentialBgDispatcher) { val newBufferOverrides = bufferOverrides.toMutableMap() val newTagOverrides = tagOverrides.toMutableMap() @@ -132,7 +132,7 @@ constructor( } fun clearAllOverrides() { - applicationScope.launch(sequentialBgDispatcher) { + applicationScope.launch(context = sequentialBgDispatcher) { bufferOverrides = emptyMap() tagOverrides = emptyMap() @@ -142,7 +142,7 @@ constructor( } private fun loadEchoOverrides() { - applicationScope.launch(sequentialBgDispatcher) { + applicationScope.launch(context = sequentialBgDispatcher) { val overrideSetting = globalSettings.getString(OVERRIDE_SETTING_PATH) ?: return@launch val overrideList = settingFormat.parseOverrides(overrideSetting) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt index 4528b047375a..2191f379e812 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt @@ -103,7 +103,7 @@ import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext // URI fields to try loading album art from diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt index 7b8703dfbe4f..591a9cccdadd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt @@ -67,7 +67,7 @@ import kotlin.coroutines.coroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.ensureActive diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt index affc7b741b2a..3821f3d8c111 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt @@ -111,7 +111,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext // URI fields to try loading album art from diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt index 7a6de5c07b43..a6e1582d4e0c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt @@ -64,7 +64,7 @@ import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig import com.android.systemui.surfaceeffects.ripple.RippleShader import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext private const val TAG = "MediaControlViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt index 8a04799d3f94..4877d18de7ab 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt @@ -58,7 +58,7 @@ import com.android.systemui.util.animation.TransitionLayout import kotlin.math.min import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext private const val TAG = "MediaRecommendationsViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt index 72650ea89dcb..43c20117b6e0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt @@ -115,7 +115,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext private const val TAG = "MediaCarouselController" diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt index 745ab12c27d6..c32bd403d2e8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt @@ -68,7 +68,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private val TAG: String = MediaHierarchyManager::class.java.simpleName diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt index 575e1986a23f..a05808e0ef0a 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -27,10 +27,10 @@ import com.android.systemui.shared.recents.model.ThumbnailData import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @MediaProjectionAppSelectorScope class MediaProjectionAppSelectorController diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt index 3b84d2c53a2b..9aa988e351d1 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt @@ -33,7 +33,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class RecentTaskViewHolder @AssistedInject diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt index dab7439f0f0c..fa3ee6f0ba2d 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt @@ -35,7 +35,7 @@ import com.android.systemui.res.R import com.android.systemui.util.NotificationChannels import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Coordinator responsible for showing/hiding the task switcher notification. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt index c392c2ff17f3..45ff7f4f87ef 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerEmptyImpl.kt @@ -18,9 +18,9 @@ package com.android.systemui.navigationbar import com.android.internal.statusbar.RegisterStatusBarResult import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.shared.statusbar.phone.BarTransitions import com.android.systemui.navigationbar.views.NavigationBar import com.android.systemui.navigationbar.views.NavigationBarView +import com.android.systemui.shared.statusbar.phone.BarTransitions import javax.inject.Inject /** A no-op version of [NavigationBarController] for variants like Arc and TV. */ @@ -30,18 +30,28 @@ class NavigationBarControllerEmptyImpl @Inject constructor() : NavigationBarCont includeDefaultDisplay: Boolean, result: RegisterStatusBarResult?, ) {} + override fun removeNavigationBar(displayId: Int) {} + override fun checkNavBarModes(displayId: Int) {} + override fun finishBarAnimations(displayId: Int) {} + override fun touchAutoDim(displayId: Int) {} + override fun transitionTo( displayId: Int, @BarTransitions.TransitionMode barMode: Int, animate: Boolean, ) {} + override fun disableAnimationsDuringHide(displayId: Int, delay: Long) {} + override fun getDefaultNavigationBarView(): NavigationBarView? = null + override fun getNavigationBarView(displayId: Int): NavigationBarView? = null + override fun isOverviewEnabled(displayId: Int) = false + override fun getDefaultNavigationBar(): NavigationBar? = null } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java index a8b979e05276..914e0f74e4a0 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java @@ -28,7 +28,6 @@ import com.android.app.viewcapture.ViewCapture; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; -import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.navigationbar.views.NavigationBarFrame; import com.android.systemui.navigationbar.views.NavigationBarView; import com.android.systemui.res.R; @@ -64,14 +63,6 @@ public interface NavigationBarModule { return barView.findViewById(R.id.navigation_bar_view); } - /** */ - @Provides - @NavigationBarScope - static EdgeBackGestureHandler provideEdgeBackGestureHandler( - EdgeBackGestureHandler.Factory factory, @DisplayId Context context) { - return factory.create(context); - } - /** A WindowManager specific to the display's context. */ @Provides @NavigationBarScope diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt index e931f8f5398a..2d001508a720 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt @@ -46,7 +46,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : private var arrowLength = AnimatedFloat( name = "arrowLength", - minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS + minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS, ) /** @@ -56,7 +56,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : var arrowHeight = AnimatedFloat( name = "arrowHeight", - minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ROTATION_DEGREES + minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ROTATION_DEGREES, ) val backgroundWidth = @@ -89,7 +89,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : AnimatedFloat( name = "scale", minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_SCALE, - minimumValue = 0f + minimumValue = 0f, ) val scalePivotX = @@ -111,7 +111,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : name = "arrowAlpha", minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA, minimumValue = 0f, - maximumValue = 1f + maximumValue = 1f, ) val backgroundAlpha = @@ -119,7 +119,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : name = "backgroundAlpha", minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA, minimumValue = 0f, - maximumValue = 1f + maximumValue = 1f, ) private val allAnimatedFloat = @@ -133,7 +133,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : scale, horizontalTranslation, arrowAlpha, - backgroundAlpha + backgroundAlpha, ) /** @@ -162,7 +162,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : com.android.internal.R.attr.materialColorOnSecondaryContainer } else { com.android.internal.R.attr.materialColorOnSecondaryFixed - } + }, ) arrowBackgroundPaint.color = @@ -172,7 +172,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : com.android.internal.R.attr.materialColorSecondaryContainer } else { com.android.internal.R.attr.materialColorSecondaryFixedDim - } + }, ) } @@ -242,7 +242,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : fun stretchTo( stretchAmount: Float, startingVelocity: Float? = null, - springForce: SpringForce? = null + springForce: SpringForce? = null, ) { animation.apply { startingVelocity?.let { @@ -303,7 +303,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : fun addAnimationEndListener( animatedFloat: AnimatedFloat, - endListener: DelayedOnAnimationEndListener + endListener: DelayedOnAnimationEndListener, ): Boolean { return if (animatedFloat.isRunning) { animatedFloat.addEndListener(endListener) @@ -327,43 +327,43 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : backgroundHeightStretchAmount: Float, edgeCornerStretchAmount: Float, farCornerStretchAmount: Float, - fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens + fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens, ) { horizontalTranslation.stretchBy( finalPosition = fullyStretchedDimens.horizontalTranslation, - amount = horizontalTranslationStretchAmount + amount = horizontalTranslationStretchAmount, ) arrowLength.stretchBy( finalPosition = fullyStretchedDimens.arrowDimens.length, - amount = arrowStretchAmount + amount = arrowStretchAmount, ) arrowHeight.stretchBy( finalPosition = fullyStretchedDimens.arrowDimens.height, - amount = arrowStretchAmount + amount = arrowStretchAmount, ) arrowAlpha.stretchBy( finalPosition = fullyStretchedDimens.arrowDimens.alpha, - amount = arrowAlphaStretchAmount + amount = arrowAlphaStretchAmount, ) backgroundAlpha.stretchBy( finalPosition = fullyStretchedDimens.backgroundDimens.alpha, - amount = backgroundAlphaStretchAmount + amount = backgroundAlphaStretchAmount, ) backgroundWidth.stretchBy( finalPosition = fullyStretchedDimens.backgroundDimens.width, - amount = backgroundWidthStretchAmount + amount = backgroundWidthStretchAmount, ) backgroundHeight.stretchBy( finalPosition = fullyStretchedDimens.backgroundDimens.height, - amount = backgroundHeightStretchAmount + amount = backgroundHeightStretchAmount, ) backgroundEdgeCornerRadius.stretchBy( finalPosition = fullyStretchedDimens.backgroundDimens.edgeCornerRadius, - amount = edgeCornerStretchAmount + amount = edgeCornerStretchAmount, ) backgroundFarCornerRadius.stretchBy( finalPosition = fullyStretchedDimens.backgroundDimens.farCornerRadius, - amount = farCornerStretchAmount + amount = farCornerStretchAmount, ) } @@ -381,7 +381,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : arrowAlpha.stretchTo( stretchAmount = 0f, startingVelocity = startingVelocity, - springForce = springForce + springForce = springForce, ) } @@ -403,7 +403,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : /** Updates resting arrow and background size not accounting for stretch */ internal fun setRestingDimens( restingParams: EdgePanelParams.BackIndicatorDimens, - animate: Boolean = true + animate: Boolean = true, ) { horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation) scale.updateRestingPosition(restingParams.scale) @@ -417,11 +417,11 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate) backgroundEdgeCornerRadius.updateRestingPosition( restingParams.backgroundDimens.edgeCornerRadius, - animate + animate, ) backgroundFarCornerRadius.updateRestingPosition( restingParams.backgroundDimens.farCornerRadius, - animate + animate, ) } @@ -483,11 +483,11 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : topLeft = edgeCorner, bottomLeft = edgeCorner, topRight = farCorner, - bottomRight = farCorner + bottomRight = farCorner, ) canvas.drawPath( arrowBackground, - arrowBackgroundPaint.apply { alpha = (255 * backgroundAlpha.pos).toInt() } + arrowBackgroundPaint.apply { alpha = (255 * backgroundAlpha.pos).toInt() }, ) val dx = arrowLength.pos @@ -498,7 +498,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : val arrowOffset = (backgroundWidth - dx) / 2 canvas.translate( /* dx= */ arrowOffset, - /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */ + /* dy= */ 0f, /* pass 0 for the y position since the canvas was already translated */ ) val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel) @@ -532,7 +532,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : topLeft: Float = 0f, topRight: Float = 0f, bottomRight: Float = 0f, - bottomLeft: Float = 0f + bottomLeft: Float = 0f, ): Path = Path().apply { val corners = @@ -544,7 +544,7 @@ class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : bottomRight, bottomRight, bottomLeft, - bottomLeft + bottomLeft, ) addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index d8c13b6e8f6d..44c828731e24 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -41,11 +41,11 @@ import com.android.systemui.plugins.NavigationEdgeBackPlugin import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.ViewController -import com.android.systemui.util.concurrency.BackPanelUiThread -import com.android.systemui.util.concurrency.UiThreadContext import com.android.systemui.util.time.SystemClock +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.io.PrintWriter -import javax.inject.Inject import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -82,11 +82,12 @@ private const val POP_ON_INACTIVE_VELOCITY = -1.5f private const val DEBUG = false class BackPanelController -internal constructor( - context: Context, +@AssistedInject +constructor( + @Assisted context: Context, private val windowManager: ViewCaptureAwareWindowManager, private val viewConfiguration: ViewConfiguration, - private val mainHandler: Handler, + @Assisted private val mainHandler: Handler, private val systemClock: SystemClock, private val vibratorHelper: VibratorHelper, private val configurationController: ConfigurationController, @@ -94,40 +95,9 @@ internal constructor( private val interactionJankMonitor: InteractionJankMonitor, ) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin { - /** - * Injectable instance to create a new BackPanelController. - * - * Necessary because EdgeBackGestureHandler sometimes needs to create new instances of - * BackPanelController, and we need to match EdgeBackGestureHandler's context. - */ - class Factory - @Inject - constructor( - private val windowManager: ViewCaptureAwareWindowManager, - private val viewConfiguration: ViewConfiguration, - @BackPanelUiThread private val uiThreadContext: UiThreadContext, - private val systemClock: SystemClock, - private val vibratorHelper: VibratorHelper, - private val configurationController: ConfigurationController, - private val latencyTracker: LatencyTracker, - private val interactionJankMonitor: InteractionJankMonitor, - ) { - /** Construct a [BackPanelController]. */ - fun create(context: Context): BackPanelController { - uiThreadContext.isCurrentThread() - return BackPanelController( - context, - windowManager, - viewConfiguration, - uiThreadContext.handler, - systemClock, - vibratorHelper, - configurationController, - latencyTracker, - interactionJankMonitor - ) - .also { it.init() } - } + @AssistedFactory + interface Factory { + fun create(context: Context, handler: Handler): BackPanelController } @VisibleForTesting internal var params: EdgePanelParams = EdgePanelParams(resources) @@ -206,7 +176,7 @@ internal constructor( COMMITTED, /* back action currently cancelling, arrow soon to be GONE */ - CANCELLED + CANCELLED, } /** @@ -224,7 +194,7 @@ internal constructor( animation: DynamicAnimation<*>, canceled: Boolean, value: Float, - velocity: Float + velocity: Float, ) { animation.removeEndListener(this) @@ -421,7 +391,7 @@ internal constructor( if ( isPastThresholdToActive( isPastThreshold = isPastStaticThreshold, - dynamicDelay = entryToActiveDelayCalculation + dynamicDelay = entryToActiveDelayCalculation, ) ) { updateArrowState(GestureState.ACTIVE) @@ -437,7 +407,7 @@ internal constructor( isPastStaticThreshold && isPastDynamicReactivationThreshold && isWithinYActivationThreshold, - delay = MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION + delay = MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION, ) ) { updateArrowState(GestureState.ACTIVE) @@ -593,7 +563,7 @@ internal constructor( arrowAlphaStretchAmount = 1f, edgeCornerStretchAmount = 1f, farCornerStretchAmount = 1f, - fullyStretchedDimens = params.fullyStretchedIndicator + fullyStretchedDimens = params.fullyStretchedIndicator, ) } @@ -608,7 +578,7 @@ internal constructor( params.entryIndicator.arrowDimens.alphaInterpolator?.get(progress)?.value ?: 0f, edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress), farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress), - fullyStretchedDimens = params.preThresholdIndicator + fullyStretchedDimens = params.preThresholdIndicator, ) } @@ -643,7 +613,7 @@ internal constructor( ?: 0f, edgeCornerStretchAmount = params.edgeCornerInterpolator.getInterpolation(progress), farCornerStretchAmount = params.farCornerInterpolator.getInterpolation(progress), - fullyStretchedDimens = params.preThresholdIndicator + fullyStretchedDimens = params.preThresholdIndicator, ) } @@ -688,7 +658,7 @@ internal constructor( private fun isPastThresholdToActive( isPastThreshold: Boolean, delay: Float? = null, - dynamicDelay: () -> Float = { delay ?: 0F } + dynamicDelay: () -> Float = { delay ?: 0F }, ): Boolean { val resetValue = 0L val isPastThresholdForFirstTime = pastThresholdWhileEntryOrInactiveTime == resetValue @@ -709,7 +679,7 @@ internal constructor( private fun playWithBackgroundWidthAnimation( onEnd: DelayedOnAnimationEndListener, - delay: Long = 0L + delay: Long = 0L, ) { if (delay == 0L) { updateRestingArrowDimens() @@ -871,8 +841,8 @@ internal constructor( GestureState.FLUNG -> params.activeIndicator.backgroundDimens GestureState.COMMITTED -> params.committedIndicator.backgroundDimens GestureState.CANCELLED -> params.cancelledIndicator.backgroundDimens - } - ) + }, + ), ) } @@ -970,7 +940,7 @@ internal constructor( } mainHandler.postDelayed( onEndSetCommittedStateListener.runnable, - MIN_DURATION_FLING_ANIMATION + MIN_DURATION_FLING_ANIMATION, ) updateRestingArrowDimens() } @@ -984,13 +954,13 @@ internal constructor( updateRestingArrowDimens() mainHandler.postDelayed( onEndSetGoneStateListener.runnable, - MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION + MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION, ) } else { mView.popScale(POP_ON_COMMITTED_VELOCITY) mainHandler.postDelayed( onAlphaEndSetGoneStateListener.runnable, - MIN_DURATION_COMMITTED_ANIMATION + MIN_DURATION_COMMITTED_ANIMATION, ) } } @@ -1008,14 +978,14 @@ internal constructor( private fun performDeactivatedHapticFeedback() { vibratorHelper.performHapticFeedback( mView, - HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE + HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE, ) } private fun performActivatedHapticFeedback() { vibratorHelper.performHapticFeedback( mView, - HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE + HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE, ) } @@ -1075,7 +1045,7 @@ internal constructor( "xDelta=${"%.1f".format(totalTouchDeltaActive)}", "xTranslation=${"%.1f".format(previousXTranslation)}", "pre=${"%.0f".format(preProgress)}%", - "post=${"%.0f".format(postProgress)}%" + "post=${"%.0f".format(postProgress)}%", ) val debugPaint = Paint().apply { color = Color.WHITE } val debugInfoBottom = debugStrings.size * 32f + 4f @@ -1084,7 +1054,7 @@ internal constructor( 4f, canvas.width.toFloat(), debugStrings.size * 32f + 4f, - debugPaint + debugPaint, ) debugPaint.apply { color = Color.BLACK @@ -1132,7 +1102,7 @@ class Step<T>( private val threshold: Float, private val factor: Float = 1.1f, private val postThreshold: T, - private val preThreshold: T + private val preThreshold: T, ) { data class Value<T>(val value: T, val isNewState: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index b3463bdc2949..53177de89733 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -103,6 +103,10 @@ import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.pip.Pip; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + import kotlinx.coroutines.Job; import java.io.PrintWriter; @@ -117,7 +121,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; -import javax.inject.Inject; import javax.inject.Provider; /** @@ -414,8 +417,21 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } }; + /** + * Factory for EdgeBackGestureHandler. Necessary because per-display contexts can't be injected. + * With this, you can pass in a specific context that knows what display it is in. + */ + @AssistedFactory + public interface Factory { + /** + * Creates a new EdgeBackGestureHandler with the given context. + */ + EdgeBackGestureHandler create(Context context); + } + + @AssistedInject EdgeBackGestureHandler( - Context context, + @Assisted Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @@ -751,7 +767,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private void resetEdgeBackPlugin() { - setEdgeBackPlugin(mBackPanelControllerFactory.create(mContext)); + BackPanelController backPanelController = mBackPanelControllerFactory.create(mContext, + mUiThreadContext.getHandler()); + backPanelController.init(); + setEdgeBackPlugin(backPanelController); } private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) { @@ -1329,113 +1348,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } } - /** - * Injectable instance to create a new EdgeBackGestureHandler. - * - * Necessary because we don't have good handling of per-display contexts at the moment. With - * this, you can pass in a specific context that knows what display it is in. - */ - public static class Factory { - private final OverviewProxyService mOverviewProxyService; - private final SysUiState mSysUiState; - private final PluginManager mPluginManager; - private final UiThreadContext mUiThreadContext; - private final Executor mBackgroundExecutor; - private final Handler mBgHandler; - private final UserTracker mUserTracker; - private final NavigationModeController mNavigationModeController; - private final BackPanelController.Factory mBackPanelControllerFactory; - private final ViewConfiguration mViewConfiguration; - private final WindowManager mWindowManager; - private final IWindowManager mWindowManagerService; - private final InputManager mInputManager; - private final Optional<Pip> mPipOptional; - private final Optional<DesktopMode> mDesktopModeOptional; - private final FalsingManager mFalsingManager; - private final Provider<BackGestureTfClassifierProvider> - mBackGestureTfClassifierProviderProvider; - private final Provider<LightBarController> mLightBarControllerProvider; - private final NotificationShadeWindowController mNotificationShadeWindowController; - - private final GestureInteractor mGestureInteractor; - - private final JavaAdapter mJavaAdapter; - - @Inject - public Factory(OverviewProxyService overviewProxyService, - SysUiState sysUiState, - PluginManager pluginManager, - @BackPanelUiThread UiThreadContext uiThreadContext, - @Background Executor backgroundExecutor, - @Background Handler bgHandler, - UserTracker userTracker, - NavigationModeController navigationModeController, - BackPanelController.Factory backPanelControllerFactory, - ViewConfiguration viewConfiguration, - WindowManager windowManager, - IWindowManager windowManagerService, - InputManager inputManager, - Optional<Pip> pipOptional, - Optional<DesktopMode> desktopModeOptional, - FalsingManager falsingManager, - Provider<BackGestureTfClassifierProvider> - backGestureTfClassifierProviderProvider, - Provider<LightBarController> lightBarControllerProvider, - NotificationShadeWindowController notificationShadeWindowController, - GestureInteractor gestureInteractor, - JavaAdapter javaAdapter) { - mOverviewProxyService = overviewProxyService; - mSysUiState = sysUiState; - mPluginManager = pluginManager; - mUiThreadContext = uiThreadContext; - mBackgroundExecutor = backgroundExecutor; - mBgHandler = bgHandler; - mUserTracker = userTracker; - mNavigationModeController = navigationModeController; - mBackPanelControllerFactory = backPanelControllerFactory; - mViewConfiguration = viewConfiguration; - mWindowManager = windowManager; - mWindowManagerService = windowManagerService; - mInputManager = inputManager; - mPipOptional = pipOptional; - mDesktopModeOptional = desktopModeOptional; - mFalsingManager = falsingManager; - mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; - mLightBarControllerProvider = lightBarControllerProvider; - mNotificationShadeWindowController = notificationShadeWindowController; - mGestureInteractor = gestureInteractor; - mJavaAdapter = javaAdapter; - } - - /** Construct a {@link EdgeBackGestureHandler}. */ - public EdgeBackGestureHandler create(Context context) { - return mUiThreadContext.runWithScissors( - () -> new EdgeBackGestureHandler( - context, - mOverviewProxyService, - mSysUiState, - mPluginManager, - mUiThreadContext, - mBackgroundExecutor, - mBgHandler, - mUserTracker, - mNavigationModeController, - mBackPanelControllerFactory, - mViewConfiguration, - mWindowManager, - mWindowManagerService, - mInputManager, - mPipOptional, - mDesktopModeOptional, - mFalsingManager, - mBackGestureTfClassifierProviderProvider, - mLightBarControllerProvider, - mNotificationShadeWindowController, - mGestureInteractor, - mJavaAdapter)); - } - } - private static class LogArray extends ArrayDeque<String> { private final int mLength; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt index db8749f59d9c..283ae7f84c59 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt @@ -16,7 +16,7 @@ data class EdgePanelParams(private var resources: Resources) { val heightSpring: SpringForce? = null, val lengthSpring: SpringForce? = null, var alphaSpring: Step<SpringForce>? = null, - var alphaInterpolator: Step<Float>? = null + var alphaInterpolator: Step<Float>? = null, ) data class BackgroundDimens( @@ -178,14 +178,14 @@ data class EdgePanelParams(private var resources: Resources) { threshold = commonArrowDimensAlphaThreshold, factor = commonArrowDimensAlphaFactor, postThreshold = createSpring(180f, 0.9f), - preThreshold = createSpring(2000f, 0.6f) + preThreshold = createSpring(2000f, 0.6f), ) val commonArrowDimensAlphaSpringInterpolator = Step( threshold = commonArrowDimensAlphaThreshold, factor = commonArrowDimensAlphaFactor, postThreshold = 1f, - preThreshold = 0f + preThreshold = 0f, ) entryIndicator = @@ -204,7 +204,7 @@ data class EdgePanelParams(private var resources: Resources) { lengthSpring = createSpring(600f, 0.4f), heightSpring = createSpring(600f, 0.4f), alphaSpring = commonArrowDimensAlphaSpring, - alphaInterpolator = commonArrowDimensAlphaSpringInterpolator + alphaInterpolator = commonArrowDimensAlphaSpringInterpolator, ), backgroundDimens = BackgroundDimens( @@ -217,7 +217,7 @@ data class EdgePanelParams(private var resources: Resources) { heightSpring = createSpring(1500f, 0.45f), farCornerRadiusSpring = createSpring(300f, 0.5f), edgeCornerRadiusSpring = createSpring(150f, 0.5f), - ) + ), ) activeIndicator = @@ -235,7 +235,7 @@ data class EdgePanelParams(private var resources: Resources) { lengthSpring = activeCommittedArrowLengthSpring, heightSpring = activeCommittedArrowHeightSpring, alphaSpring = commonArrowDimensAlphaSpring, - alphaInterpolator = commonArrowDimensAlphaSpringInterpolator + alphaInterpolator = commonArrowDimensAlphaSpringInterpolator, ), backgroundDimens = BackgroundDimens( @@ -248,7 +248,7 @@ data class EdgePanelParams(private var resources: Resources) { heightSpring = createSpring(10000f, 1f), edgeCornerRadiusSpring = createSpring(2600f, 0.855f), farCornerRadiusSpring = createSpring(1200f, 0.30f), - ) + ), ) preThresholdIndicator = @@ -266,7 +266,7 @@ data class EdgePanelParams(private var resources: Resources) { lengthSpring = createSpring(100f, 0.6f), heightSpring = createSpring(100f, 0.6f), alphaSpring = commonArrowDimensAlphaSpring, - alphaInterpolator = commonArrowDimensAlphaSpringInterpolator + alphaInterpolator = commonArrowDimensAlphaSpringInterpolator, ), backgroundDimens = BackgroundDimens( @@ -281,7 +281,7 @@ data class EdgePanelParams(private var resources: Resources) { heightSpring = createSpring(1500f, 0.45f), farCornerRadiusSpring = createSpring(300f, 1f), edgeCornerRadiusSpring = createSpring(250f, 0.5f), - ) + ), ) committedIndicator = @@ -317,7 +317,7 @@ data class EdgePanelParams(private var resources: Resources) { lengthSpring = createSpring(850f, 0.46f), heightSpring = createSpring(850f, 0.46f), length = activeIndicator.arrowDimens.length, - height = activeIndicator.arrowDimens.height + height = activeIndicator.arrowDimens.height, ), backgroundDimens = committedIndicator.backgroundDimens.copy( @@ -325,7 +325,7 @@ data class EdgePanelParams(private var resources: Resources) { heightSpring = flungCommittedHeightSpring, edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring, farCornerRadiusSpring = flungCommittedFarCornerSpring, - ) + ), ) cancelledIndicator = @@ -334,7 +334,7 @@ data class EdgePanelParams(private var resources: Resources) { entryIndicator.backgroundDimens.copy( width = 0f, alpha = 0f, - alphaSpring = createSpring(450f, 1f) + alphaSpring = createSpring(450f, 1f), ) ) @@ -366,7 +366,7 @@ data class EdgePanelParams(private var resources: Resources) { heightSpring = null, edgeCornerRadiusSpring = null, farCornerRadiusSpring = null, - ) + ), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt index 0166176721c3..774a0669fe2d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt @@ -36,7 +36,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -55,7 +55,7 @@ constructor( ) { enum class Scope { Local, - Global + Global, } private val _localGestureBlockedMatchers = MutableStateFlow<Set<TaskMatcher>>(setOf()) @@ -86,7 +86,7 @@ constructor( combine( _topActivity, gestureRepository.gestureBlockedMatchers, - _localGestureBlockedMatchers.asStateFlow() + _localGestureBlockedMatchers.asStateFlow(), ) { runningTask, global, local -> runningTask != null && (global + local).any { it.matches(runningTask) } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/NavbarOrientationTrackingLogger.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/NavbarOrientationTrackingLogger.kt index a5ba17b98a40..251fd2e5954c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/NavbarOrientationTrackingLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/buttons/NavbarOrientationTrackingLogger.kt @@ -31,7 +31,7 @@ constructor(@NavbarOrientationTrackingLog private val buffer: LogBuffer) { isImmersiveMode: Boolean, isSecondaryHandleVisible: Boolean, currentRotation: Int, - startingQuickSwitchRotation: Int + startingQuickSwitchRotation: Int, ) { buffer.log( TAG, @@ -52,7 +52,7 @@ constructor(@NavbarOrientationTrackingLog private val buffer: LogBuffer) { "\tDelta Rotation: ${getDeltaRotation(int1, int2)}\n" + "\tStarting QuickSwitch Rotation: $int1\n" + "\tCurrent Rotation: $int2\n" - } + }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt index 0e5404164ba1..7178d095d230 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Models UI state used to render the content of the notifications shade overlay. diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt index 954e94af1c1a..2f7e9160e281 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt @@ -29,7 +29,7 @@ import com.android.compose.theme.PlatformTheme import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** People Tile Widget configuration activity that shows the user their conversation tiles. */ class PeopleSpaceActivity diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 4323b3199d57..8b0694219630 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -31,7 +31,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Adapter to determine what real class to use for classes that depend on [QSHost]. diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 6db91ac073ba..e4738a23b73b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -131,7 +131,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SuppressLint("ValidFragment") class QSFragmentCompose @@ -221,7 +221,7 @@ constructor( { notificationScrimClippingParams.params.top }, // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). - { scrollState.canScrollForward && viewModel.isQsFullyExpanded }, + { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing }, ) frame.addView( composeView, diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index e21485b3e85a..d571dd04e024 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -65,7 +65,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @OptIn(ExperimentalCoroutinesApi::class) class QSFragmentComposeViewModel diff --git a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt new file mode 100644 index 000000000000..ffeec4e0480c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt @@ -0,0 +1,93 @@ +/* + * 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.qs.flags + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils +import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.shade.shared.flag.DualShade + +/** Helper for reading or using the QS Detailed View flag state. */ +@Suppress("NOTHING_TO_INLINE") +object QsDetailedView { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_QS_TILE_DETAILED_VIEW + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the flag enabled */ + @JvmStatic + inline val isEnabled + get() = + Flags.qsTileDetailedView() && // mainAconfigFlag + DualShade.isEnabled && + SceneContainerFlag.isEnabled + + // NOTE: Changes should also be made in getSecondaryFlags + + /** The main aconfig flag. */ + inline fun getMainAconfigFlag() = FlagToken(FLAG_NAME, Flags.qsTileDetailedView()) + + /** The set of secondary flags which must be enabled for qs detailed view to work properly */ + inline fun getSecondaryFlags(): Sequence<FlagToken> = + sequenceOf( + DualShade.token + // NOTE: Changes should also be made in isEnabled + ) + SceneContainerFlag.getAllRequirements() + + /** The full set of requirements for QsDetailedView */ + inline fun getAllRequirements(): Sequence<FlagToken> { + return sequenceOf(getMainAconfigFlag()) + getSecondaryFlags() + } + + /** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */ + inline fun getFlagDependencies(): Sequence<Pair<FlagToken, FlagToken>> { + val mainAconfigFlag = getMainAconfigFlag() + return getSecondaryFlags().map { mainAconfigFlag to it } + } + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) + + /** Returns a developer-readable string that describes the current requirement list. */ + @JvmStatic + fun requirementDescription(): String { + return buildString { + getAllRequirements().forEach { requirement -> + append('\n') + append(if (requirement.isEnabled) " [MET]" else "[NOT MET]") + append(" ${requirement.name}") + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index b0d4fa26225e..564bc78a3f98 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val TAG = "FooterActionsViewModel" diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt index ef30cbfd55e5..43fd0f5feec7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt @@ -33,8 +33,6 @@ import com.android.systemui.qs.panels.ui.compose.GridLayout import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout -import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModel -import com.android.systemui.qs.panels.ui.viewmodel.IconLabelVisibilityViewModelImpl import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModelImpl import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsSizeViewModelImpl @@ -63,11 +61,6 @@ interface PanelsModule { @Binds fun bindQSColumnsViewModel(impl: QSColumnsSizeViewModelImpl): QSColumnsViewModel @Binds - fun bindIconLabelVisibilityViewModel( - impl: IconLabelVisibilityViewModelImpl - ): IconLabelVisibilityViewModel - - @Binds @PaginatedBaseLayoutType fun bindPaginatedBaseGridLayout(impl: InfiniteGridLayout): PaginatableGridLayout diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt deleted file mode 100644 index fe4012787394..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.qs.panels.domain.interactor - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.log.LogBuffer -import com.android.systemui.log.core.LogLevel -import com.android.systemui.qs.panels.shared.model.PanelsLog -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn - -@SysUISingleton -class IconLabelVisibilityInteractor -@Inject -constructor( - private val preferencesInteractor: QSPreferencesInteractor, - @PanelsLog private val logBuffer: LogBuffer, - @Application scope: CoroutineScope, -) { - val showLabels: StateFlow<Boolean> = - preferencesInteractor.showLabels - .onEach { logChange(it) } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) - - fun setShowLabels(showLabels: Boolean) { - preferencesInteractor.setShowLabels(showLabels) - } - - private fun logChange(showLabels: Boolean) { - logBuffer.log( - LOG_BUFFER_ICON_TILE_LABEL_VISIBILITY_CHANGE_TAG, - LogLevel.DEBUG, - { bool1 = showLabels }, - { "Icon tile label visibility changed: $bool1" } - ) - } - - private companion object { - const val LOG_BUFFER_ICON_TILE_LABEL_VISIBILITY_CHANGE_TAG = "IconLabelVisibilityChange" - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt index 854e23f16a13..22543b12b93c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt @@ -24,13 +24,8 @@ import kotlinx.coroutines.flow.Flow @SysUISingleton class QSPreferencesInteractor @Inject constructor(private val repo: QSPreferencesRepository) { - val showLabels: Flow<Boolean> = repo.showLabels val largeTilesSpecs: Flow<Set<TileSpec>> = repo.largeTilesSpecs - fun setShowLabels(showLabels: Boolean) { - repo.setShowLabels(showLabels) - } - fun setLargeTilesSpecs(specs: Set<TileSpec>) { repo.setLargeTilesSpecs(specs) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt index 0dedfe125d6f..91f1477d5325 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PagerDots.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import platform.test.motion.compose.values.MotionTestValueKey import platform.test.motion.compose.values.motionTestValues diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index 1c7a334d3ef2..99a6cda8cbf5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -73,6 +73,7 @@ fun SceneScope.QuickQuickSettings( squishiness = { squishiness }, coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), + tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index 71fa0ac30fb7..978a3534e95b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -23,6 +23,7 @@ import androidx.compose.animation.graphics.res.animatedVectorResource import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -57,8 +58,8 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.toggleableState import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.android.compose.modifiers.background import com.android.compose.modifiers.thenIf +import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.common.ui.compose.load @@ -77,27 +78,27 @@ fun LargeTileContent( colors: TileColors, squishiness: () -> Float, accessibilityUiState: AccessibilityUiState? = null, - toggleClickSupported: Boolean = false, iconShape: Shape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius), - onClick: () -> Unit = {}, - onLongClick: () -> Unit = {}, + toggleClick: (() -> Unit)? = null, + onLongClick: (() -> Unit)? = null, ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = tileHorizontalArrangement(), ) { // Icon - val longPressLabel = longPressLabel() + val longPressLabel = longPressLabel().takeIf { onLongClick != null } Box( modifier = - Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) { + Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClick != null) { Modifier.clip(iconShape) .verticalSquish(squishiness) - .background(colors.iconBackground, { 1f }) + .background(colors.iconBackground) .combinedClickable( - onClick = onClick, + onClick = toggleClick!!, onLongClick = onLongClick, onLongClickLabel = longPressLabel, + hapticFeedbackEnabled = !Flags.msdlFeedback(), ) .thenIf(accessibilityUiState != null) { Modifier.semantics { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index d2ec958c17b7..418ed0be293f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -104,7 +104,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastMap import com.android.compose.animation.bounceable -import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.systemui.common.ui.compose.load import com.android.systemui.qs.panels.shared.model.SizedTile @@ -137,7 +136,7 @@ import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.res.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object TileType @@ -202,14 +201,17 @@ fun DefaultEditTileGrid( topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) }, ) { innerPadding -> CompositionLocalProvider(LocalOverscrollConfiguration provides null) { + val scrollState = rememberScrollState() + LaunchedEffect(listState.dragInProgress) { + if (listState.dragInProgress) { + scrollState.animateScrollTo(0) + } + } + Column( verticalArrangement = spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)), - modifier = - modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(innerPadding), + modifier = modifier.fillMaxSize().verticalScroll(scrollState).padding(innerPadding), ) { AnimatedContent( targetState = listState.dragInProgress, @@ -432,8 +434,10 @@ fun LazyGridScope.EditTiles( // If the tile is being moved, replace it with a visible spacer SpacerGridCell( Modifier.background( - color = MaterialTheme.colorScheme.secondary, - alpha = { EditModeTileDefaults.PLACEHOLDER_ALPHA }, + color = + MaterialTheme.colorScheme.secondary.copy( + alpha = EditModeTileDefaults.PLACEHOLDER_ALPHA + ), shape = RoundedCornerShape(InactiveCornerRadius), ) .animateItem() diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 366bc9a7a29a..91f2da2b90f7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.grid.ui.compose.VerticalSpannedGrid +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout @@ -49,6 +50,7 @@ class InfiniteGridLayout constructor( private val iconTilesViewModel: IconTilesViewModel, private val viewModelFactory: InfiniteGridViewModel.Factory, + private val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, ) : PaginatableGridLayout { @Composable @@ -92,6 +94,7 @@ constructor( iconOnly = iconTilesViewModel.isIconTile(it.tile.spec), modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider, coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 52d526123430..e1583e3fa3d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -64,9 +64,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable import com.android.compose.animation.bounceable import com.android.compose.modifiers.thenIf +import com.android.systemui.Flags import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.compose.modifiers.sysuiResTag +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModel +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider +import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius @@ -78,7 +82,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import java.util.function.Supplier import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private const val TEST_TAG_SMALL = "qs_tile_small" private const val TEST_TAG_LARGE = "qs_tile_large" @@ -109,6 +113,7 @@ fun Tile( squishiness: () -> Float, coroutineScope: CoroutineScope, bounceableInfo: BounceableInfo, + tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, modifier: Modifier = Modifier, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) @@ -116,6 +121,10 @@ fun Tile( val resources = resources() val uiState = remember(state, resources) { state.toUiState(resources) } val colors = TileDefaults.getColorForState(uiState) + val hapticsViewModel: TileHapticsViewModel? = + rememberViewModel(traceName = "TileHapticsViewModel") { + tileHapticsViewModelFactoryProvider.getHapticsViewModelFactory()?.create(tile) + } // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) @@ -129,6 +138,7 @@ fun Tile( }, shape = tileShape, squishiness = squishiness, + hapticsViewModel = hapticsViewModel, modifier = modifier .fillMaxWidth() @@ -143,11 +153,19 @@ fun Tile( TileContainer( onClick = { tile.onClick(expandable) + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.CLICKED + ) if (uiState.accessibilityUiState.toggleableState != null) { coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } } }, - onLongClick = { tile.onLongClick(expandable) }, + onLongClick = { + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.LONG_CLICKED + ) + tile.onLongClick(expandable) + }, uiState = uiState, iconOnly = iconOnly, ) { @@ -160,19 +178,30 @@ fun Tile( ) } else { val iconShape = TileDefaults.animateIconShape(uiState.state) + val secondaryClick: (() -> Unit)? = + { + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.CLICKED + ) + tile.onSecondaryClick() + } + .takeIf { uiState.handlesSecondaryClick } + val longClick: (() -> Unit)? = + { + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.LONG_CLICKED + ) + tile.onLongClick(expandable) + } + .takeIf { uiState.handlesLongClick } LargeTileContent( label = uiState.label, secondaryLabel = uiState.secondaryLabel, icon = icon, colors = colors, iconShape = iconShape, - toggleClickSupported = state.handlesSecondaryClick, - onClick = { - if (state.handlesSecondaryClick) { - tile.onSecondaryClick() - } - }, - onLongClick = { tile.onLongClick(expandable) }, + toggleClick = secondaryClick, + onLongClick = longClick, accessibilityUiState = uiState.accessibilityUiState, squishiness = squishiness, ) @@ -186,6 +215,7 @@ private fun TileExpandable( color: Color, shape: Shape, squishiness: () -> Float, + hapticsViewModel: TileHapticsViewModel?, modifier: Modifier = Modifier, content: @Composable (Expandable) -> Unit, ) { @@ -194,7 +224,7 @@ private fun TileExpandable( shape = shape, modifier = modifier.clip(shape).verticalSquish(squishiness), ) { - content(it) + content(hapticsViewModel?.createStateAwareExpandable(it) ?: it) } } @@ -255,6 +285,7 @@ fun Modifier.tileCombinedClickable( onLongClick = onLongClick, onClickLabel = uiState.accessibilityUiState.clickLabel, onLongClickLabel = longPressLabel, + hapticFeedbackEnabled = !Flags.msdlFeedback(), ) .semantics { role = uiState.accessibilityUiState.accessibilityRole diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt deleted file mode 100644 index 12cbde2cbc91..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.qs.panels.ui.viewmodel - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.qs.panels.domain.interactor.IconLabelVisibilityInteractor -import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow - -interface IconLabelVisibilityViewModel { - val showLabels: StateFlow<Boolean> - - fun setShowLabels(showLabels: Boolean) -} - -@SysUISingleton -class IconLabelVisibilityViewModelImpl -@Inject -constructor( - private val interactor: IconLabelVisibilityInteractor, -) : IconLabelVisibilityViewModel { - override val showLabels: StateFlow<Boolean> = interactor.showLabels - - override fun setShowLabels(showLabels: Boolean) { - interactor.setShowLabels(showLabels) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt index 78212b2ac3cc..0f7dafcd941c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt @@ -30,13 +30,9 @@ class PaginatedGridViewModel constructor( iconTilesViewModel: IconTilesViewModel, gridSizeViewModel: QSColumnsViewModel, - iconLabelVisibilityViewModel: IconLabelVisibilityViewModel, paginatedGridInteractor: PaginatedGridInteractor, @Application applicationScope: CoroutineScope, -) : - IconTilesViewModel by iconTilesViewModel, - QSColumnsViewModel by gridSizeViewModel, - IconLabelVisibilityViewModel by iconLabelVisibilityViewModel { +) : IconTilesViewModel by iconTilesViewModel, QSColumnsViewModel by gridSizeViewModel { val rows = paginatedGridInteractor.rows.stateIn( applicationScope, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt index 72b586a2f4df..887a70f39f6a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.panels.ui.viewmodel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider import com.android.systemui.qs.panels.domain.interactor.QuickQuickSettingsRowInteractor import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl @@ -45,6 +46,7 @@ constructor( val squishinessViewModel: TileSquishinessViewModel, private val iconTilesViewModel: IconTilesViewModel, @Application private val applicationScope: CoroutineScope, + val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, ) { val columns = qsColumnsViewModel.columns diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt index aa420800be7b..56675e49d4e6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt @@ -33,6 +33,7 @@ data class TileUiState( val label: String, val secondaryLabel: String, val state: Int, + val handlesLongClick: Boolean, val handlesSecondaryClick: Boolean, val icon: Supplier<QSTile.Icon?>, val accessibilityUiState: AccessibilityUiState, @@ -86,6 +87,7 @@ fun QSTile.State.toUiState(resources: Resources): TileUiState { label = label?.toString() ?: "", secondaryLabel = secondaryLabel?.toString() ?: "", state = if (disabledByPolicy) Tile.STATE_UNAVAILABLE else state, + handlesLongClick = handlesLongClick, handlesSecondaryClick = handlesSecondaryClick, icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null }, AccessibilityUiState( diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt index 9fcb0db0c88d..f41507f86f7f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -70,7 +70,7 @@ constructor( } private fun startFlowCollections(autoAdded: StateFlow<Set<TileSpec>>) { - applicationScope.launch(bgDispatcher) { + applicationScope.launch(context = bgDispatcher) { launch { autoAdded.collect { store(it) } } launch { // As Settings is not the source of truth, once we started tracking tiles for a diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt index b0ae1e1abf21..64da853484ff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -84,7 +84,7 @@ constructor( } private fun startFlowCollections(tiles: StateFlow<List<TileSpec>>) { - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { launch { tiles.collect { storeTiles(userId, it) } } launch { // As Settings is not the source of truth, once we started tracking tiles for a diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt index 88784bf79ce6..56c3e0e648bf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AccessibilityTilesInteractor.kt @@ -30,7 +30,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Observe the tiles in the QS Panel and perform accessibility related actions */ @SysUISingleton @@ -55,7 +55,7 @@ constructor( } private fun startObservingTiles(currentTilesInteractor: CurrentTilesInteractor) { - scope.launch(backgroundDispatcher) { + scope.launch(context = backgroundDispatcher) { currentTilesInteractor.currentTiles .sample(currentTilesInteractor.userContext) { currentTiles, userContext -> Data(currentTiles.map(TileModel::spec), userContext) diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt index 187b4445637b..b11868ac5334 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.take -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Collects the signals coming from all registered [AutoAddable] and adds/removes tiles accordingly. diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index 10097d6eef7e..2bb6bba51dbd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -63,7 +63,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** @@ -207,7 +207,7 @@ constructor( } } - launch(backgroundDispatcher) { + launch(context = backgroundDispatcher) { userAndTiles.collectLatest { val newUser = it.userId val newTileList = it.tiles @@ -289,7 +289,7 @@ constructor( } override fun addTile(spec: TileSpec, position: Int) { - scope.launch(backgroundDispatcher) { + scope.launch(context = backgroundDispatcher) { // Block until the list is not empty currentTiles.filter { it.isNotEmpty() }.first() tileSpecRepository.addTile(userRepository.getSelectedUserInfo().id, spec, position) diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt index a5be14ec3776..180e0feea2ab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.take -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Interactor in charge of triggering reconciliation after QS Secure Settings are restored. For a @@ -42,7 +42,7 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) fun start() { - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { qsSettingsRestoredRepository.restoreData .flatMapConcat { data -> autoAddRepository.autoAddedTiles(data.userId).take(1).map { tiles -> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt index cf2db6c66ce7..1c9cb3d99480 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt @@ -48,7 +48,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.runBlocking class ModesTile diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index fb406d47d7a6..1792ebd5245d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -152,7 +152,14 @@ constructor( recordingController.startCountdown( DELAY_MS, INTERVAL_MS, - pendingServiceIntent(getStartIntent(userContextProvider.userContext)), + pendingServiceIntent( + getStartIntent( + userContextProvider.userContext, + issueRecordingState.traceConfig, + issueRecordingState.recordScreen, + issueRecordingState.takeBugreport, + ) + ), pendingServiceIntent(getStopIntent(userContextProvider.userContext)), ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt index a2430ad45274..28167b3fa076 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt @@ -25,7 +25,7 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Utility for binding tiles to kotlin flows. Similar to [JavaAdapter] and usable for QS tiles. We diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt index d8c5af289048..87f542e6ab39 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt @@ -61,7 +61,7 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt index 92b0f3aa604d..0ebd6f2b4ac3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch interface CustomTilePackageUpdatesRepository { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt index 1b3e58524815..8f870d468997 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @QSTileScope @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt index 3e507cda4805..6f1cb3cc2675 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt index 79e903c7bce9..ccc84c091b1b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.channels.produce import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Communicates with [TileService] via [TileServiceManager] and [IQSTileService]. This interactor is diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt index 0c8a3750f6fe..fceee5a3379e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent +import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC import com.android.systemui.screenrecord.RecordingController @@ -52,6 +53,7 @@ class IssueRecordingUserActionInteractor @Inject constructor( @Main private val mainCoroutineContext: CoroutineContext, + private val state: IssueRecordingState, private val keyguardDismissUtil: KeyguardDismissUtil, private val keyguardStateController: KeyguardStateController, private val dialogTransitionAnimator: DialogTransitionAnimator, @@ -104,8 +106,15 @@ constructor( recordingController.startCountdown( DELAY_MS, INTERVAL_MS, - pendingServiceIntent(getStartIntent(userContextProvider.userContext)), - pendingServiceIntent(getStopIntent(userContextProvider.userContext)) + pendingServiceIntent( + getStartIntent( + userContextProvider.userContext, + state.traceConfig, + state.recordScreen, + state.takeBugreport, + ) + ), + pendingServiceIntent(getStopIntent(userContextProvider.userContext)), ) private fun stopIssueRecordingService() = @@ -117,6 +126,6 @@ constructor( userContextProvider.userContext, RecordingService.REQUEST_CODE, action, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt index 14115444fe49..d46bcfc3b947 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt @@ -32,7 +32,7 @@ import com.android.systemui.statusbar.policy.LocationController import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Handles location tile clicks. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt index f03c7521931c..671943c5baff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.DataSaverController import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class DataSaverDialogDelegate( private val sysuiDialogFactory: SystemUIDialog.Factory, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt index 8077c67bbba6..f89745f49cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.takeWhile -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch // TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout class QSTileViewModelAdapter diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt index ac6ebe7c87d3..6d5bf328d00b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt @@ -57,7 +57,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext // TODO(307945185) Split View concerns into a ViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt index f77386dbe91b..06d3e4a9ad0e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Models UI state needed for rendering the content of the quick settings scene. diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt index afb9a788ec24..bed857496f0b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Models UI state used to render the content of the quick settings shade overlay. diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index 3f875bcc288b..1eac0e1a75fa 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -24,11 +24,10 @@ import android.content.res.Resources import android.net.Uri import android.os.Handler import android.os.IBinder -import android.os.Looper +import android.os.UserHandle import android.util.Log import com.android.internal.logging.UiEventLogger import com.android.systemui.animation.DialogTransitionAnimator -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.LongRunning import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor @@ -36,8 +35,12 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.RecordingService import com.android.systemui.screenrecord.RecordingServiceStrings +import com.android.systemui.screenrecord.ScreenMediaRecorder.SavedRecording import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE +import com.android.traceur.PresetTraceConfigs +import com.android.traceur.TraceConfig import java.util.concurrent.Executor import javax.inject.Inject @@ -45,7 +48,6 @@ class IssueRecordingService @Inject constructor( controller: RecordingController, - @Background private val bgLooper: Looper, @LongRunning private val bgExecutor: Executor, @Main handler: Handler, uiEventLogger: UiEventLogger, @@ -57,6 +59,7 @@ constructor( private val issueRecordingState: IssueRecordingState, traceurConnectionProvider: TraceurConnection.Provider, iActivityManager: IActivityManager, + screenRecordingStartTimeStore: ScreenRecordingStartTimeStore, ) : RecordingService( controller, @@ -66,6 +69,7 @@ constructor( notificationManager, userContextProvider, keyguardDismissUtil, + screenRecordingStartTimeStore, ) { private val traceurConnection: TraceurConnection = traceurConnectionProvider.create() @@ -80,6 +84,7 @@ constructor( iActivityManager, notificationManager, userContextProvider, + screenRecordingStartTimeStore, ) /** @@ -109,15 +114,23 @@ constructor( Log.d(getTag(), "handling action: ${intent?.action}") when (intent?.action) { ACTION_START -> { - session.start() - if (!issueRecordingState.recordScreen) { + val screenRecord = intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false) + with(session) { + traceConfig = + intent.getParcelableExtra(INTENT_EXTRA_TRACE_TYPE, TraceConfig::class.java) + ?: PresetTraceConfigs.getDefaultConfig() + takeBugReport = intent.getBooleanExtra(EXTRA_BUG_REPORT, false) + this.screenRecord = screenRecord + start() + } + if (!screenRecord) { // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action // will circumvent the RecordingService's screen recording start code. return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId) } } ACTION_STOP, - ACTION_STOP_NOTIF -> session.stop(contentResolver) + ACTION_STOP_NOTIF -> session.stop() ACTION_SHARE -> { session.share( intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId), @@ -133,9 +146,23 @@ constructor( return super.onStartCommand(intent, flags, startId) } + /** + * If the user chooses to create a bugreport, we do not want to make them click share twice. To + * avoid that, the code immediately triggers the bugreport flow which will handle the rest. + */ + override fun onRecordingSaved(recording: SavedRecording?, currentUser: UserHandle) { + if (session.takeBugReport) { + session.share(mNotificationId, recording?.uri) + } else { + super.onRecordingSaved(recording, currentUser) + } + } + companion object { private const val TAG = "IssueRecordingService" private const val CHANNEL_ID = "issue_record" + const val EXTRA_SCREEN_RECORD = "extra_screenRecord" + const val EXTRA_BUG_REPORT = "extra_bugReport" /** * Get an intent to stop the issue recording service. @@ -153,8 +180,17 @@ constructor( * * @param context Context from the requesting activity */ - fun getStartIntent(context: Context): Intent = - Intent(context, IssueRecordingService::class.java).setAction(ACTION_START) + fun getStartIntent( + context: Context, + traceConfig: TraceConfig, + screenRecord: Boolean, + bugReport: Boolean, + ): Intent = + Intent(context, IssueRecordingService::class.java) + .setAction(ACTION_START) + .putExtra(INTENT_EXTRA_TRACE_TYPE, traceConfig) + .putExtra(EXTRA_SCREEN_RECORD, screenRecord) + .putExtra(EXTRA_BUG_REPORT, bugReport) } } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt index ad9b4fe164e8..43539335d8e4 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt @@ -18,15 +18,17 @@ package com.android.systemui.recordissue import android.app.IActivityManager import android.app.NotificationManager -import android.content.ContentResolver +import android.content.Intent import android.net.Uri import android.os.UserHandle import android.provider.Settings import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.settings.UserContextProvider +import com.android.traceur.PresetTraceConfigs import java.util.concurrent.Executor +private const val SHELL_PACKAGE = "com.android.shell" private const val NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended" private const val DISABLED = 0 @@ -46,17 +48,25 @@ class IssueRecordingServiceSession( private val iActivityManager: IActivityManager, private val notificationManager: NotificationManager, private val userContextProvider: UserContextProvider, + private val startTimeStore: ScreenRecordingStartTimeStore, ) { + var takeBugReport = false + var traceConfig = PresetTraceConfigs.getDefaultConfig() + var screenRecord = false fun start() { - bgExecutor.execute { traceurConnection.startTracing(issueRecordingState.traceConfig) } + bgExecutor.execute { traceurConnection.startTracing(traceConfig) } issueRecordingState.isRecording = true } - fun stop(contentResolver: ContentResolver) { + fun stop() { bgExecutor.execute { - if (issueRecordingState.traceConfig.longTrace) { - Settings.Global.putInt(contentResolver, NOTIFY_SESSION_ENDED_SETTING, DISABLED) + if (traceConfig.longTrace) { + Settings.Global.putInt( + userContextProvider.userContext.contentResolver, + NOTIFY_SESSION_ENDED_SETTING, + DISABLED, + ) } traceurConnection.stopTracing() } @@ -70,11 +80,24 @@ class IssueRecordingServiceSession( notificationId, UserHandle(userContextProvider.userContext.userId), ) - - if (issueRecordingState.takeBugreport) { - iActivityManager.requestBugReportWithExtraAttachment(screenRecording) + val screenRecordingUris: List<Uri> = + mutableListOf<Uri>().apply { + screenRecording?.let { add(it) } + if (traceConfig.winscope && screenRecord) { + startTimeStore.getFileUri(userContextProvider.userContext)?.let { add(it) } + } + } + if (takeBugReport) { + screenRecordingUris.forEach { + userContextProvider.userContext.grantUriPermission( + SHELL_PACKAGE, + it, + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ) + } + iActivityManager.requestBugReportWithExtraAttachments(screenRecordingUris) } else { - traceurConnection.shareTraces(screenRecording) + traceurConnection.shareTraces(screenRecordingUris) } } diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStore.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStore.kt new file mode 100644 index 000000000000..5d8bc5564f10 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenRecordingStartTimeStore.kt @@ -0,0 +1,78 @@ +/* + * 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.recordissue + +import android.content.Context +import android.net.Uri +import android.os.SystemClock +import android.util.Log +import android.util.SparseArray +import androidx.annotation.VisibleForTesting +import androidx.core.content.FileProvider +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.settings.UserTracker +import java.io.File +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import org.json.JSONObject + +private const val TAG = "ScreenRecordingStartTimeStore" +@VisibleForTesting const val REAL_TO_ELAPSED_TIME_OFFSET_NANOS_KEY = "realToElapsedTimeOffsetNanos" +@VisibleForTesting const val ELAPSED_REAL_TIME_NANOS_KEY = "elapsedRealTimeNanos" +private const val RECORDING_METADATA_FILE_SUFFIX = "screen_recording_metadata.json" +private const val AUTHORITY = "com.android.systemui.fileprovider" + +@SysUISingleton +class ScreenRecordingStartTimeStore @Inject constructor(private val userTracker: UserTracker) { + @VisibleForTesting val userIdToScreenRecordingStartTime = SparseArray<JSONObject>() + + fun markStartTime() { + val elapsedRealTimeNano = SystemClock.elapsedRealtimeNanos() + val realToElapsedTimeOffsetNano = + TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()) - + SystemClock.elapsedRealtimeNanos() + val startTimeMetadata = + JSONObject() + .put(ELAPSED_REAL_TIME_NANOS_KEY, elapsedRealTimeNano) + .put(REAL_TO_ELAPSED_TIME_OFFSET_NANOS_KEY, realToElapsedTimeOffsetNano) + userIdToScreenRecordingStartTime.put(userTracker.userId, startTimeMetadata) + } + + /** + * Outputs start time metadata as Json to a file that can then be shared. Returns the Uri or + * null if the file system is not usable and the start time meta data is available. Uses + * com.android.systemui.fileprovider's authority. + * + * Because this file is not uniquely named, it doesn't need to be cleaned up. Every time it is + * outputted, it will overwrite the last file's contents. This is a feature, not a bug. + */ + fun getFileUri(context: Context): Uri? { + val dir = context.externalCacheDir?.apply { mkdirs() } ?: return null + try { + val outFile = + File(dir, RECORDING_METADATA_FILE_SUFFIX).apply { + userIdToScreenRecordingStartTime.get(userTracker.userId)?.let { + writeText(it.toString()) + } ?: return null + } + return FileProvider.getUriForFile(context, AUTHORITY, outFile) + } catch (e: Exception) { + Log.e(TAG, "failed to get screen recording start time metadata via file uri", e) + return null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt index 81529b357527..e6df3cd59ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt @@ -76,8 +76,9 @@ private constructor(userContextProvider: UserContextProvider, private val bgLoop @WorkerThread fun stopTracing() = sendMessage(MessageConstants.STOP_WHAT) @WorkerThread - fun shareTraces(screenRecord: Uri?) { - val replyHandler = Messenger(ShareFilesHandler(screenRecord, userContextProvider, bgLooper)) + fun shareTraces(screenRecordingUris: List<Uri>) { + val replyHandler = + Messenger(ShareFilesHandler(screenRecordingUris, userContextProvider, bgLooper)) sendMessage(MessageConstants.SHARE_WHAT, replyTo = replyHandler) } @@ -101,7 +102,7 @@ private constructor(userContextProvider: UserContextProvider, private val bgLoop } private class ShareFilesHandler( - private val screenRecord: Uri?, + private val screenRecordingUris: List<Uri>, private val userContextProvider: UserContextProvider, looper: Looper, ) : Handler(looper) { @@ -122,7 +123,7 @@ private class ShareFilesHandler( ArrayList<Uri>().apply { perfetto?.let { add(it) } winscope?.let { add(it) } - screenRecord?.let { add(it) } + screenRecordingUris.forEach { add(it) } } val fileSharingIntent = FileSender.buildSendIntent(userContextProvider.userContext, uris) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt index e477efe0808e..ba789a01d285 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Business logic about the visibility of various parts of the window root view. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt index 6d1c1a7b5bfe..7939404251d1 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/KeyguardStateCallbackStartable.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Keeps all [IKeyguardStateCallback]s hydrated with the latest state. */ @@ -76,7 +76,7 @@ constructor( callbacks.add(callback) - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { callback.onShowingStateChanged( !deviceEntryInteractor.isDeviceEntered.value, selectedUserInteractor.getSelectedUserId(), diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 286cac10fa05..5229acc5f876 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -102,7 +102,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt index e352bfe938f6..1d970349b955 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt @@ -52,7 +52,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class ScrimStartable @@ -185,6 +185,7 @@ constructor( } else if (isOnKeyguard && !unlocking && isDreaming) { Model(scrimState = ScrimState.DREAMING, unlocking = false) } else { + onKeyguardFadedAway(transitionState.isIdle(Scenes.Gone)) Model(scrimState = ScrimState.UNLOCKED, unlocking = unlocking) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt index d7413687eeae..1d8dc4f661da 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @SysUISingleton @@ -160,7 +160,7 @@ constructor( } override fun onBootCompleted() { - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { try { statusBarService.disableForUser( StatusBarManager.DISABLE_NONE, diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index 47254775618c..82f65cf55211 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Models UI state for the scene container. */ class SceneContainerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index c3de06778ee5..6cc9ae4fb674 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -43,7 +43,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget; +import com.android.systemui.recordissue.ScreenRecordingStartTimeStore; import com.android.systemui.res.R; +import com.android.systemui.screenrecord.ScreenMediaRecorder.SavedRecording; import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -92,6 +94,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private boolean mShowTaps; private boolean mOriginalShowTaps; private ScreenMediaRecorder mRecorder; + private final ScreenRecordingStartTimeStore mScreenRecordingStartTimeStore; private final Executor mLongExecutor; private final UiEventLogger mUiEventLogger; protected final NotificationManager mNotificationManager; @@ -103,7 +106,8 @@ public class RecordingService extends Service implements ScreenMediaRecorderList public RecordingService(RecordingController controller, @LongRunning Executor executor, @Main Handler handler, UiEventLogger uiEventLogger, NotificationManager notificationManager, - UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { + UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil, + ScreenRecordingStartTimeStore screenRecordingStartTimeStore) { mController = controller; mLongExecutor = executor; mMainHandler = handler; @@ -111,6 +115,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; mKeyguardDismissUtil = keyguardDismissUtil; + mScreenRecordingStartTimeStore = screenRecordingStartTimeStore; } /** @@ -178,7 +183,8 @@ public class RecordingService extends Service implements ScreenMediaRecorderList currentUid, mAudioSource, captureTarget, - this + this, + mScreenRecordingStartTimeStore ); if (startRecording()) { @@ -379,8 +385,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList } @VisibleForTesting - protected Notification createSaveNotification( - @Nullable ScreenMediaRecorder.SavedRecording recording) { + protected Notification createSaveNotification(@Nullable SavedRecording recording) { Uri uri = recording != null ? recording.getUri() : null; Intent viewIntent = new Intent(Intent.ACTION_VIEW) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION) @@ -501,15 +506,13 @@ public class RecordingService extends Service implements ScreenMediaRecorderList mLongExecutor.execute(() -> { try { Log.d(getTag(), "saving recording"); - Notification notification = createSaveNotification( - getRecorder() != null ? getRecorder().save() : null); + SavedRecording savedRecording = getRecorder() != null ? getRecorder().save() : null; postGroupSummaryNotification( currentUser, strings().getSaveTitle(), GROUP_KEY_SAVED, NOTIF_GROUP_ID_SAVED); - mNotificationManager.notifyAsUser(null, mNotificationId, notification, - currentUser); + onRecordingSaved(savedRecording, currentUser); } catch (IOException | IllegalStateException e) { Log.e(getTag(), "Error saving screen recording: " + e.getMessage()); e.printStackTrace(); @@ -519,6 +522,12 @@ public class RecordingService extends Service implements ScreenMediaRecorderList }); } + protected void onRecordingSaved(ScreenMediaRecorder.SavedRecording savedRecording, + UserHandle currentUser) { + mNotificationManager.notifyAsUser(null, mNotificationId, + createSaveNotification(savedRecording), currentUser); + } + private void setTapsVisible(boolean turnOn) { int value = turnOn ? 1 : 0; Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index e024710ed3eb..54da1b04aeb4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -55,6 +55,7 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget; +import com.android.systemui.recordissue.ScreenRecordingStartTimeStore; import java.io.Closeable; import java.io.File; @@ -91,6 +92,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; private final MediaProjectionCaptureTarget mCaptureRegion; + private final ScreenRecordingStartTimeStore mScreenRecordingStartTimeStore; private final Handler mHandler; private Context mContext; @@ -99,13 +101,15 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { public ScreenMediaRecorder(Context context, Handler handler, int uid, ScreenRecordingAudioSource audioSource, MediaProjectionCaptureTarget captureRegion, - ScreenMediaRecorderListener listener) { + ScreenMediaRecorderListener listener, + ScreenRecordingStartTimeStore screenRecordingStartTimeStore) { mContext = context; mHandler = handler; mUid = uid; mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; + mScreenRecordingStartTimeStore = screenRecordingStartTimeStore; } private void prepare() throws IOException, RemoteException, RuntimeException { @@ -278,6 +282,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { Log.d(TAG, "start recording"); prepare(); mMediaRecorder.start(); + mScreenRecordingStartTimeStore.markStartTime(); recordInternalAudio(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt b/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt index 746f0f348431..d7aad31050d7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/AnnouncementResolver.kt @@ -23,7 +23,7 @@ import com.android.systemui.screenshot.resources.Messages import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Logic for determining the announcement that a screenshot has been taken (for accessibility). */ class AnnouncementResolver diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt index 56afb79c40d4..28bff51dc041 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt @@ -14,7 +14,7 @@ import com.android.systemui.res.R import com.android.systemui.screenshot.message.ProfileMessageController import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * MessageContainerController controls the display of content in the screenshot message container. diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index 7aeec47241cb..f243c2711bfc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt index a7557463b12f..6cdaf2938983 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt @@ -37,7 +37,7 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch interface TakeScreenshotExecutor { suspend fun executeScreenshots( diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt index 0fefa0b7757a..68ff094b62f7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt @@ -42,7 +42,7 @@ import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel import com.android.systemui.util.children import javax.inject.Inject import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class ScreenshotShelfViewBinder @Inject diff --git a/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt b/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt index 8f4402eaa406..7e967f436ecb 100644 --- a/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/security/data/repository/SecurityRepository.kt @@ -26,7 +26,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch interface SecurityRepository { /** The current [SecurityModel]. */ diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 1863e12187cd..fc4db0877dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.sync.Mutex /** @@ -204,7 +204,7 @@ internal constructor( if (isBackgroundUserSwitchEnabled) { userSwitchingJob?.cancel() userSwitchingJob = - appScope.launch(backgroundContext) { + appScope.launch(context = backgroundContext) { handleUserSwitchingCoroutines(newUserId) { reply?.sendResult(null) } } } else { @@ -218,7 +218,7 @@ internal constructor( if (isBackgroundUserSwitchEnabled) { afterUserSwitchingJob?.cancel() afterUserSwitchingJob = - appScope.launch(backgroundContext) { + appScope.launch(context = backgroundContext) { handleUserSwitchComplete(newUserId) } } else { @@ -260,10 +260,10 @@ internal constructor( for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) { val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue - launch(callbackDataItem.executor.asCoroutineDispatcher()) { + launch(context = callbackDataItem.executor.asCoroutineDispatcher()) { val mutex = Mutex(true) val thresholdLogJob = - launch(backgroundContext) { + launch(context = backgroundContext) { delay(USER_CHANGE_THRESHOLD) Log.e(TAG, "Failed to finish $callback in time") } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 52cb8d6df7e1..49ceba834dd4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -70,7 +70,7 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Controller that's responsible for the glanceable hub container view and its touch handling. diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt index c74f038ebea4..38de17eb7fd8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt @@ -22,7 +22,7 @@ import com.android.systemui.res.R import javax.inject.Inject import kotlin.math.max -class LargeScreenHeaderHelper @Inject constructor(private val context: Context) { +class LargeScreenHeaderHelper @Inject constructor(@ShadeDisplayAware private val context: Context) { fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index f463cb5bcbd6..6e63446d88d8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -34,7 +34,7 @@ import javax.inject.Inject class NotificationPanelUnfoldAnimationController @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, statusBarStateController: StatusBarStateController, progressProvider: NaturalRotationUnfoldProgressProvider, ) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 750f5ad62bab..083cf1fc8b17 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -668,7 +668,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Handler handler, - LayoutInflater layoutInflater, + @ShadeDisplayAware LayoutInflater layoutInflater, FeatureFlags featureFlags, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, @@ -689,7 +689,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ShadeLogger shadeLogger, - ConfigurationController configurationController, + @ShadeDisplayAware ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index f83548ddbf45..24dba59a1d54 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -148,13 +148,13 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW @Inject public NotificationShadeWindowControllerImpl( - Context context, + @ShadeDisplayAware Context context, WindowRootViewComponent.Factory windowRootViewComponentFactory, ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, IActivityManager activityManager, DozeParameters dozeParameters, StatusBarStateController statusBarStateController, - ConfigurationController configurationController, + @ShadeDisplayAware ConfigurationController configurationController, KeyguardViewMediator keyguardViewMediator, KeyguardBypassController keyguardBypassController, @Main Executor mainExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java index 64085fdef961..f5fc1f414f82 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java @@ -24,6 +24,7 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.LayoutRes; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; @@ -53,6 +54,8 @@ import com.android.app.viewcapture.ViewCaptureFactory; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.floatingtoolbar.FloatingToolbar; import com.android.systemui.scene.ui.view.WindowRootView; +import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; /** * Combined keyguard and notification panel view. Also holding backdrop and scrims. This view can @@ -68,6 +71,7 @@ public class NotificationShadeWindowView extends WindowRootView { private ActionMode mFloatingActionMode; private FloatingToolbar mFloatingToolbar; private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; + @Nullable private ConfigurationForwarder mConfigurationForwarder; private InteractionEventHandler mInteractionEventHandler; @@ -162,6 +166,20 @@ public class NotificationShadeWindowView extends WindowRootView { } @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mConfigurationForwarder != null) { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode(); + mConfigurationForwarder.onConfigurationChanged(newConfig); + } + } + + public void setConfigurationForwarder(ConfigurationForwarder configurationForwarder) { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode(); + mConfigurationForwarder = configurationForwarder; + } + + @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 36449bee8bf6..365666df7540 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -57,6 +57,7 @@ import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; +import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -69,6 +70,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.phone.DozeScrimController; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; @@ -188,7 +190,8 @@ public class NotificationShadeWindowViewController implements Dumpable { QuickSettingsController quickSettingsController, PrimaryBouncerInteractor primaryBouncerInteractor, AlternateBouncerInteractor alternateBouncerInteractor, - BouncerViewBinder bouncerViewBinder) { + BouncerViewBinder bouncerViewBinder, + @ShadeDisplayAware ConfigurationForwarder configurationForwarder) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -245,6 +248,9 @@ public class NotificationShadeWindowViewController implements Dumpable { mDisableSubpixelTextTransitionListener)); } + if (ShadeWindowGoesAround.isEnabled()) { + mView.setConfigurationForwarder(configurationForwarder); + } dumpManager.registerDumpable(this); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index 348b6bab1617..437d32d53b60 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -49,7 +49,7 @@ import dagger.Lazy import java.util.function.Consumer import javax.inject.Inject import kotlin.reflect.KMutableProperty0 -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt index 1fcb70c6bc95..7a70966c2b12 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt @@ -2,9 +2,9 @@ package com.android.systemui.shade import android.content.Context import android.view.DisplayCutout -import com.android.systemui.res.R import com.android.systemui.battery.BatteryMeterView -import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import javax.inject.Inject /** @@ -14,10 +14,12 @@ import javax.inject.Inject class QsBatteryModeController @Inject constructor( - private val context: Context, - private val insetsProvider: StatusBarContentInsetsProvider, + @ShadeDisplayAware private val context: Context, + insetsProviderStore: StatusBarContentInsetsProviderStore, ) { + private val insetsProvider = insetsProviderStore.defaultDisplay + private companion object { // MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end // frames. @@ -65,6 +67,5 @@ constructor( private fun hasCenterCutout(cutout: DisplayCutout?): Boolean = cutout?.let { !insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty - } - ?: false + } ?: false } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt index 6c99282bdcdd..49fa80c02d21 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt new file mode 100644 index 000000000000..111d335d0d0f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.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.shade + +import javax.inject.Qualifier + +/** + * Qualifies classes that provide display-specific info for shade window components. + * + * The Shade window can be moved between displays with different characteristics (e.g., density, + * size). This annotation ensures that components within the shade window use the correct context + * and resources for the display they are currently on. + * + * Classes annotated with `@ShadeDisplayAware` (e.g., 'Context`, `Resources`, `LayoutInflater`, + * `ConfigurationController`) will be dynamically updated to reflect the current display's + * configuration. This ensures consistent rendering even when the shade window is moved to an + * external display. + */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeDisplayAware diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt new file mode 100644 index 000000000000..51f1f81ab1f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -0,0 +1,104 @@ +/* + * 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.shade + +import android.content.Context +import android.content.res.Resources +import android.view.LayoutInflater +import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY +import com.android.systemui.common.ui.GlobalConfig +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R +import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround +import com.android.systemui.statusbar.phone.ConfigurationControllerImpl +import com.android.systemui.statusbar.phone.ConfigurationForwarder +import com.android.systemui.statusbar.policy.ConfigurationController +import dagger.Module +import dagger.Provides + +/** + * Module responsible for managing display-specific components and resources for the notification + * shade window. + * + * This isolation is crucial because when the window transitions between displays, its associated + * context, resources, and display characteristics (like density and size) also change. If the shade + * window shared the same context as the rest of the system UI, it could lead to inconsistencies and + * errors due to incorrect display information. + * + * By using this dedicated module, we ensure the notification shade window always utilizes the + * correct display context and resources, regardless of the display it's on. + */ +@Module +object ShadeDisplayAwareModule { + + /** Creates a new context for the shade window. */ + @Provides + @ShadeDisplayAware + @SysUISingleton + fun provideShadeDisplayAwareContext(context: Context): Context { + return if (ShadeWindowGoesAround.isEnabled) { + context + .createWindowContext(context.display, TYPE_APPLICATION_OVERLAY, /* options= */ null) + .apply { setTheme(R.style.Theme_SystemUI) } + } else { + context + } + } + + @Provides + @ShadeDisplayAware + @SysUISingleton + fun provideShadeDisplayAwareResources(@ShadeDisplayAware context: Context): Resources { + return context.resources + } + + @Provides + @ShadeDisplayAware + @SysUISingleton + fun providesDisplayAwareLayoutInflater(@ShadeDisplayAware context: Context): LayoutInflater { + return LayoutInflater.from(context) + } + + @Provides + @ShadeDisplayAware + @SysUISingleton + fun provideShadeWindowConfigurationController( + @ShadeDisplayAware shadeContext: Context, + factory: ConfigurationControllerImpl.Factory, + @GlobalConfig globalConfigConfigController: ConfigurationController, + ): ConfigurationController { + return if (ShadeWindowGoesAround.isEnabled) { + factory.create(shadeContext) + } else { + globalConfigConfigController + } + } + + @Provides + @ShadeDisplayAware + @SysUISingleton + fun provideShadeWindowConfigurationForwarder( + @ShadeDisplayAware shadeConfigurationController: ConfigurationController, + @GlobalConfig globalConfigController: ConfigurationController, + ): ConfigurationForwarder { + return if (ShadeWindowGoesAround.isEnabled) { + shadeConfigurationController + } else { + globalConfigController + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index cb589aa10cd9..e8a792c30aa2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -57,7 +57,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONS import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER import com.android.systemui.shade.carrier.ShadeCarrierGroup import com.android.systemui.shade.carrier.ShadeCarrierGroupController -import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory @@ -90,8 +90,8 @@ constructor( private val statusBarIconController: StatusBarIconController, private val tintedIconManagerFactory: TintedIconManager.Factory, private val privacyIconsController: HeaderPrivacyIconsController, - private val insetsProvider: StatusBarContentInsetsProvider, - private val configurationController: ConfigurationController, + private val insetsProviderStore: StatusBarContentInsetsProviderStore, + @ShadeDisplayAware private val configurationController: ConfigurationController, private val variableDateViewControllerFactory: VariableDateViewController.Factory, @Named(SHADE_HEADER) private val batteryMeterViewController: BatteryMeterViewController, private val dumpManager: DumpManager, @@ -104,6 +104,8 @@ constructor( private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory, ) : ViewController<View>(header), Dumpable { + private val insetsProvider = insetsProviderStore.defaultDisplay + companion object { /** IDs for transitions and constraints for the [MotionLayout]. */ @VisibleForTesting internal val HEADER_TRANSITION_ID = R.id.header_transition @@ -262,7 +264,7 @@ constructor( left, header.paddingTop, header.paddingRight, - header.paddingBottom + header.paddingBottom, ) systemIconsHoverContainer.setPaddingRelative( resources.getDimensionPixelSize( @@ -276,7 +278,7 @@ constructor( ), resources.getDimensionPixelSize( R.dimen.hover_system_icons_container_padding_bottom - ) + ), ) } @@ -317,7 +319,7 @@ constructor( batteryIcon.updateColors( fgColor /* foreground */, bgColor /* background */, - fgColor /* single tone (current default) */ + fgColor, /* single tone (current default) */ ) carrierIconSlots = @@ -426,7 +428,7 @@ constructor( if (view.isLayoutRtl) cutoutRight else cutoutLeft, header.paddingStart, if (view.isLayoutRtl) cutoutLeft else cutoutRight, - header.paddingEnd + header.paddingEnd, ) if (cutout != null) { @@ -437,7 +439,7 @@ constructor( changes += combinedShadeHeadersConstraintManager.centerCutoutConstraints( view.isLayoutRtl, - (view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2 + (view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2, ) } } else { @@ -563,7 +565,7 @@ constructor( clockPaddingStart, clock.paddingTop, clockPaddingEnd, - clock.paddingBottom + clock.paddingBottom, ) } @@ -602,9 +604,8 @@ constructor( @VisibleForTesting internal fun simulateViewDetached() = this.onViewDetached() - inner class CustomizerAnimationListener( - private val enteringCustomizing: Boolean, - ) : AnimatorListenerAdapter() { + inner class CustomizerAnimationListener(private val enteringCustomizing: Boolean) : + AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) header.animate().setListener(null) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 2348a110eb3a..72a465030bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -49,7 +49,10 @@ import dagger.Provides import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class]) +@Module( + includes = + [StartShadeModule::class, ShadeViewProviderModule::class] +) abstract class ShadeModule { companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt index 5afc5398d401..15b22700072f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt @@ -83,7 +83,7 @@ abstract class ShadeViewProviderModule { @Provides @SysUISingleton fun providesWindowRootView( - layoutInflater: LayoutInflater, + @ShadeDisplayAware layoutInflater: LayoutInflater, viewModelFactory: SceneContainerViewModel.Factory, containerConfigProvider: Provider<SceneContainerConfig>, scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>, @@ -156,7 +156,7 @@ abstract class ShadeViewProviderModule { @Provides fun providesKeyguardBottomAreaView( npv: NotificationPanelView, - layoutInflater: LayoutInflater, + @ShadeDisplayAware layoutInflater: LayoutInflater, ): KeyguardBottomAreaView { return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false) as KeyguardBottomAreaView @@ -250,7 +250,7 @@ abstract class ShadeViewProviderModule { fun providesBatteryMeterViewController( @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView, userTracker: UserTracker, - configurationController: ConfigurationController, + @ShadeDisplayAware configurationController: ConfigurationController, tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java index a171d33ddb47..4b8cc00e1c28 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java @@ -44,6 +44,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; @@ -153,7 +154,7 @@ public class ShadeCarrierGroupController { ShadeCarrierGroupControllerLogger logger, NetworkController networkController, CarrierTextManager.Builder carrierTextManagerBuilder, - Context context, + @ShadeDisplayAware Context context, CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, @@ -497,7 +498,7 @@ public class ShadeCarrierGroupController { ShadeCarrierGroupControllerLogger logger, NetworkController networkController, CarrierTextManager.Builder carrierTextControllerBuilder, - Context context, + @ShadeDisplayAware Context context, CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt index 193056c19d4e..5629938deeb0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt @@ -182,7 +182,7 @@ interface ShadeRepository { /** Business logic for shade interactions */ @SysUISingleton -class ShadeRepositoryImpl @Inject constructor(@Application applicationContext: Context) : +class ShadeRepositoryImpl @Inject constructor() : ShadeRepository { private val _qsExpansion = MutableStateFlow(0f) @Deprecated("Use ShadeInteractor.qsExpansion instead") 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 0902c3936661..a1c36929e77a 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 @@ -24,7 +24,6 @@ import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.data.repository.ShadeRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.currentCoroutineContext @@ -45,7 +44,6 @@ class ShadeInteractorLegacyImpl constructor( @Application val scope: CoroutineScope, keyguardRepository: KeyguardRepository, - sharedNotificationContainerInteractor: SharedNotificationContainerInteractor, repository: ShadeRepository, ) : BaseShadeInteractor { init { @@ -62,17 +60,17 @@ constructor( keyguardRepository.statusBarState, repository.legacyShadeExpansion, repository.qsExpansion, - sharedNotificationContainerInteractor.isSplitShadeEnabled, + repository.isShadeLayoutWide, ) { lockscreenShadeExpansion, statusBarState, legacyShadeExpansion, qsExpansion, - splitShadeEnabled -> + isShadeLayoutWide -> when (statusBarState) { // legacyShadeExpansion is 1 instead of 0 when QS is expanded StatusBarState.SHADE -> - if (!splitShadeEnabled && qsExpansion > 0f) 1f - qsExpansion + if (!isShadeLayoutWide && qsExpansion > 0f) 1f - qsExpansion else legacyShadeExpansion StatusBarState.KEYGUARD -> lockscreenShadeExpansion // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt index ea76ac4b0f83..50b5607f1955 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt @@ -28,7 +28,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext class ShadeLockscreenInteractorImpl diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt index 330f53fc3701..e5d08a0ac977 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt @@ -26,6 +26,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.dagger.ShadeTouchLog import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo import com.android.systemui.shade.data.repository.ShadeRepository @@ -41,14 +42,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class ShadeStartable @Inject constructor( @Application private val applicationScope: CoroutineScope, - @Application private val applicationContext: Context, + @ShadeDisplayAware private val context: Context, @ShadeTouchLog private val touchLog: LogBuffer, private val configurationRepository: ConfigurationRepository, private val shadeRepository: ShadeRepository, @@ -94,7 +95,7 @@ constructor( // Force initial collection. .onStart { emit(Unit) } .collect { - val resources = applicationContext.resources + val resources = context.resources // The configuration for 'shouldUseSplitNotificationShade' dictates the width of // the shade in both split-shade and dual-shade modes. shadeRepository.setShadeLayoutWide( diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt new file mode 100644 index 000000000000..6f492cfaa6c3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt @@ -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.systemui.shade.shared.flag + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the shade window goes around flag state. */ +@Suppress("NOTHING_TO_INLINE") +object ShadeWindowGoesAround { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_SHADE_WINDOW_GOES_AROUND + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.shadeWindowGoesAround() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is enabled. This will throw an exception if + * the flag is not enabled to ensure that the refactor author catches issues in testing. + * Caution!! Using this check incorrectly will cause crashes in nextfood builds! + */ + @JvmStatic + inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt index 4ba56749de91..a5caa0963bc0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt @@ -19,6 +19,7 @@ package com.android.systemui.shade.transition import android.content.Context import android.content.res.Configuration import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject @@ -28,11 +29,11 @@ import javax.inject.Inject internal class LargeScreenShadeInterpolatorImpl @Inject internal constructor( - configurationController: ConfigurationController, - private val context: Context, + @ShadeDisplayAware configurationController: ConfigurationController, + @ShadeDisplayAware private val context: Context, private val splitShadeInterpolator: SplitShadeInterpolator, private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator, - private val splitShadeStateController: SplitShadeStateController + private val splitShadeStateController: SplitShadeStateController, ) : LargeScreenShadeInterpolator { private var inSplitShade = false diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index bd4ed5b45dc7..45516aa69cd7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -30,6 +30,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItem import com.android.systemui.res.R import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -47,13 +48,13 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Models UI state for the shade header. */ class ShadeHeaderViewModel @AssistedInject constructor( - context: Context, + @ShadeDisplayAware context: Context, private val activityStarter: ActivityStarter, private val shadeInteractor: ShadeInteractor, private val mobileIconsInteractor: MobileIconsInteractor, diff --git a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt index dd7942503211..e6d865150d9d 100644 --- a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt @@ -22,7 +22,7 @@ import androidx.slice.SliceViewManager import com.android.systemui.common.coroutine.ConflatedCallbackFlow import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Returns updating [Slice] for a [sliceUri]. It's null when there is no slice available for the diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/ui/binder/SmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/smartspace/ui/binder/SmartspaceViewBinder.kt index 6c3d7dfc1e4f..59a0e8205123 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/ui/binder/SmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/ui/binder/SmartspaceViewBinder.kt @@ -22,7 +22,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds the view and view-model for the smartspace. */ object SmartspaceViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index f99d8f140670..520cbf9d80d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -214,7 +214,7 @@ public class KeyguardIndicationController { private boolean mEnableBatteryDefender; private boolean mIncompatibleCharger; private int mChargingWattage; - private int mBatteryLevel; + private int mBatteryLevel = -1; private boolean mBatteryPresent = true; protected long mChargingTimeRemaining; private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn; @@ -1032,12 +1032,16 @@ public class KeyguardIndicationController { } else if (!TextUtils.isEmpty(mTransientIndication)) { newIndication = mTransientIndication; } else if (!mBatteryPresent) { - // If there is no battery detected, hide the indication and bail + // If there is no battery detected, hide the indication area and bail mIndicationArea.setVisibility(GONE); return; } else if (!TextUtils.isEmpty(mAlignmentIndication)) { useMisalignmentColor = true; newIndication = mAlignmentIndication; + } else if (mBatteryLevel == -1) { + // If the battery level is not initialized, hide the indication area + mIndicationArea.setVisibility(GONE); + return; } else if (mPowerPluggedIn || mEnableBatteryDefender) { newIndication = computePowerIndication(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java index 321b6084831e..31022d578b5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java @@ -128,7 +128,8 @@ public class RemoteInputNotificationRebuilder { // Read the whole remoteInputs list from the entry, then append all of those to the sbn. Parcelable[] oldHistoryItems = sbn.getNotification().extras - .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( @@ -144,7 +145,8 @@ public class RemoteInputNotificationRebuilder { ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) : new RemoteInputHistoryItem(remoteInputText); Parcelable[] oldHistoryItems = sbn.getNotification().extras - .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Stream.of(newItem), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index 8ce0dbf8e171..be733d4e1e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel import dagger.Binds import dagger.Module import dagger.Provides @@ -31,8 +31,8 @@ import dagger.multibindings.IntoMap abstract class StatusBarChipsModule { @Binds @IntoMap - @ClassKey(DemoNotifChipViewModel::class) - abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable + @ClassKey(DemoRonChipViewModel::class) + abstract fun binds(impl: DemoRonChipViewModel): CoreStartable companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt index c5f78d2e6dd4..8abe1d329a63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Interactor for media projection events, used to show chips in the status bar for share-to-app and diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt index 5fa19ddef1be..cce9a1624d51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException @@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable import com.android.systemui.CoreStartable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel @@ -37,25 +37,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** - * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb - * commands sent by the user. + * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on + * adb commands sent by the user. * * Example adb commands: * * To show a chip with the SysUI icon and custom text and color: * ``` - * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343" + * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343" * ``` * * To hide the chip: * ``` - * adb shell cmd statusbar demo-notif --hide + * adb shell cmd statusbar demo-ron --hide * ``` * - * See [DemoNotifCommand] for more information on the adb command spec. + * See [DemoRonCommand] for more information on the adb command spec. */ @SysUISingleton -class DemoNotifChipViewModel +class DemoRonChipViewModel @Inject constructor( private val commandRegistry: CommandRegistry, @@ -63,19 +63,19 @@ constructor( private val systemClock: SystemClock, ) : OngoingActivityChipViewModel, CoreStartable { override fun start() { - commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() } + commandRegistry.registerCommand("demo-ron") { DemoRonCommand() } } private val _chip = MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden()) override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow() - private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) { + private inner class DemoRonCommand : ParseableCommand("demo-ron") { private val packageName: String? by param( longName = "packageName", shortName = "p", - description = "The package name for app \"posting\" the demo notification", + description = "The package name for the demo RON app", valueParser = Type.String, ) @@ -99,12 +99,15 @@ constructor( ) private val hide by - flag(longName = "hide", description = "Hides any existing demo notification chip") + flag( + longName = "hide", + description = "Hides any existing demo RON chip", + ) override fun execute(pw: PrintWriter) { - if (!StatusBarNotifChips.isEnabled) { + if (!StatusBarRonChips.isEnabled) { pw.println( - "Error: com.android.systemui.status_bar_notification_chips must be enabled " + + "Error: com.android.systemui.status_bar_ron_chips must be enabled " + "before using this demo feature" ) return @@ -164,12 +167,8 @@ constructor( return null } return OngoingActivityChipModel.ChipIcon.FullColorAppIcon( - Icon.Loaded(drawable = iconDrawable, contentDescription = null) + Icon.Loaded(drawable = iconDrawable, contentDescription = null), ) } } - - companion object { - private const val DEMO_COMMAND_NAME = "demo-notif" - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt index 47ffbafe3735..4ef190991f19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.notification.shared +package com.android.systemui.statusbar.chips.ron.shared import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the status bar promoted notification chips flag state. */ +/** Helper for reading or using the status bar RON chips flag state. */ @Suppress("NOTHING_TO_INLINE") -object StatusBarNotifChips { +object StatusBarRonChips { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object StatusBarNotifChips { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.statusBarNotificationChips() + get() = Flags.statusBarRonChips() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt index 28a8385e5f77..9c53cc13f702 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Interactor for the screen recording chip shown in the status bar. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt index f4462a434477..2220caab00f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt @@ -28,7 +28,7 @@ import android.widget.TextView import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.view.ChipChronometer @@ -102,7 +102,7 @@ object OngoingActivityChipBinder { defaultIconView.tintView(iconTint) } is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> { - StatusBarNotifChips.assertInNewMode() + StatusBarRonChips.assertInNewMode() IconViewBinder.bind(icon.impl, defaultIconView) defaultIconView.visibility = View.VISIBLE defaultIconView.untintView() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index cf07af1fc5dd..2366572f25e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -20,7 +20,7 @@ import android.view.View import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips /** Model representing the display of an ongoing activity as a chip in the status bar. */ sealed class OngoingActivityChipModel { @@ -91,7 +91,10 @@ sealed class OngoingActivityChipModel { override val onClickListener: View.OnClickListener?, ) : Shown(icon, colors, onClickListener) { init { - StatusBarNotifChips.assertInNewMode() + check(StatusBarRonChips.isEnabled) { + "OngoingActivityChipModel.Shown.ShortTimeDelta created even though " + + "Flags.statusBarRonChips is not enabled" + } } override val logName = "Shown.ShortTimeDelta" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt index 92e72c29519a..c8507093649b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelper.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * A class that can help [OngoingActivityChipViewModel] instances with various transition states. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index ed325970ebb2..954386e3259f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.chips.ui.viewmodel +import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -23,9 +24,9 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel @@ -56,7 +57,7 @@ constructor( castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, notifChipsViewModel: NotifChipsViewModel, - demoNotifChipViewModel: DemoNotifChipViewModel, + demoRonChipViewModel: DemoRonChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -65,8 +66,8 @@ constructor( CastToOtherDevice, Call, Notification, - /** A demo of a notification chip, used just for testing. */ - DemoNotification, + /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */ + DemoRon, } /** Model that helps us internally track the various chip states from each of the types. */ @@ -88,7 +89,7 @@ constructor( val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, val notifs: OngoingActivityChipModel.Hidden, - val demoNotif: OngoingActivityChipModel.Hidden, + val demoRon: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -98,7 +99,7 @@ constructor( val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val notifs: List<OngoingActivityChipModel.Shown> = emptyList(), - val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), + val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), ) /** Bundles all the incoming chips into one object to easily pass to various flows. */ @@ -109,8 +110,8 @@ constructor( castToOtherDeviceChipViewModel.chip, callChipViewModel.chip, notifChipsViewModel.chips, - demoNotifChipViewModel.chip, - ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif -> + demoRonChipViewModel.chip, + ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoRon -> logger.log( TAG, LogLevel.INFO, @@ -128,9 +129,9 @@ constructor( str1 = call.logName // TODO(b/364653005): Log other information for notification chips. str2 = notifs.map { it.logName }.toString() - str3 = demoNotif.logName + str3 = demoRon.logName }, - { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" }, + { "... > Call=$str1 > Notifs=$str2 > DemoRon=$str3" }, ) ChipBundle( screenRecord = screenRecord, @@ -138,7 +139,7 @@ constructor( castToOtherDevice = castToOtherDevice, call = call, notifs = notifs, - demoNotif = demoNotif, + demoRon = demoRon, ) } // Some of the chips could have timers in them and we don't want the start time @@ -197,9 +198,9 @@ constructor( * actually displaying the chip. */ val chips: StateFlow<MultipleOngoingActivityChipsModel> = - if (!StatusBarNotifChips.isEnabled) { - // Multiple chips are only allowed with notification chips. If the flag isn't on, use - // just the primary chip. + if (!Flags.statusBarRonChips()) { + // Multiple chips are only allowed with RONs. If the flag isn't on, use just the + // primary chip. primaryChip .map { MultipleOngoingActivityChipsModel( @@ -281,12 +282,11 @@ constructor( remainingChips = bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)), ) - bundle.demoNotif is OngoingActivityChipModel.Shown -> { - StatusBarNotifChips.assertInNewMode() + bundle.demoRon is OngoingActivityChipModel.Shown -> { + StatusBarRonChips.assertInNewMode() MostImportantChipResult( - mostImportantChip = - InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif), - remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()), + mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon), + remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()), ) } else -> { @@ -296,7 +296,7 @@ constructor( check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden) check(bundle.call is OngoingActivityChipModel.Hidden) check(bundle.notifs.isEmpty()) - check(bundle.demoNotif is OngoingActivityChipModel.Hidden) + check(bundle.demoRon is OngoingActivityChipModel.Hidden) MostImportantChipResult( mostImportantChip = InternalChipModel.Hidden( @@ -305,7 +305,7 @@ constructor( castToOtherDevice = bundle.castToOtherDevice, call = bundle.call, notifs = OngoingActivityChipModel.Hidden(), - demoNotif = bundle.demoNotif, + demoRon = bundle.demoRon, ), // All the chips are already hidden, so no need to filter anything out of the // bundle. @@ -334,7 +334,7 @@ constructor( ChipType.CastToOtherDevice -> new.castToOtherDevice ChipType.Call -> new.call ChipType.Notification -> new.notifs - ChipType.DemoNotification -> new.demoNotif + ChipType.DemoRon -> new.demoRon } } else if (new is InternalChipModel.Shown) { // If we have a chip to show, always show it. @@ -356,7 +356,7 @@ constructor( castToOtherDevice = OngoingActivityChipModel.Hidden(), call = OngoingActivityChipModel.Hidden(), notifs = OngoingActivityChipModel.Hidden(), - demoNotif = OngoingActivityChipModel.Hidden(), + demoRon = OngoingActivityChipModel.Hidden(), ) private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt index b64a577ef77e..e1159220e366 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -29,7 +29,7 @@ import com.android.systemui.util.kotlin.pairwiseBy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Responsible for creating and starting the status bar components for each display. Also does it diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 6201ca553398..3abbc6e0d1cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -103,9 +103,7 @@ constructor( } override fun start() { - if (StatusBarSimpleFragment.isEnabled) { - doStart() - } + doStart() } override fun initializeStatusBar() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt index 5e59745cad29..f33b76b17f96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Class responsible for managing the lifecycle and state of the status bar. @@ -139,7 +139,7 @@ constructor( } override fun start() { - StatusBarSimpleFragment.assertInNewMode() + StatusBarConnectedDisplays.assertInNewMode() coroutineScope .launch { dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt index e6270b8740a6..8a850b0fb199 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/StatusBarDataLayerModule.kt @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.data import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryModule +import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule import com.android.systemui.statusbar.data.repository.RemoteInputRepositoryModule import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerModule +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStoreModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryModule import com.android.systemui.statusbar.phone.data.StatusBarPhoneDataLayerModule import dagger.Module @@ -26,8 +28,10 @@ import dagger.Module includes = [ KeyguardStatusBarRepositoryModule::class, + PrivacyDotViewControllerStoreModule::class, RemoteInputRepositoryModule::class, StatusBarConfigurationControllerModule::class, + StatusBarContentInsetsProviderStoreModule::class, StatusBarModeRepositoryModule::class, StatusBarPhoneDataLayerModule::class, ] diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt new file mode 100644 index 000000000000..bd61c44e3d9b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt @@ -0,0 +1,108 @@ +/* + * 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.statusbar.data.repository + +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayScopeRepository +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.statusbar.events.PrivacyDotViewController +import com.android.systemui.statusbar.events.PrivacyDotViewControllerImpl +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +/** Provides per display instances of [PrivacyDotViewController]. */ +interface PrivacyDotViewControllerStore : PerDisplayStore<PrivacyDotViewController> + +@SysUISingleton +class MultiDisplayPrivacyDotViewControllerStore +@Inject +constructor( + @Background backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, + private val factory: PrivacyDotViewControllerImpl.Factory, + private val displayScopeRepository: DisplayScopeRepository, + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val contentInsetsProviderStore: StatusBarContentInsetsProviderStore, +) : + PrivacyDotViewControllerStore, + PerDisplayStoreImpl<PrivacyDotViewController>(backgroundApplicationScope, displayRepository) { + + override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController { + return factory.create( + displayScopeRepository.scopeForDisplay(displayId), + statusBarConfigurationControllerStore.forDisplay(displayId), + contentInsetsProviderStore.forDisplay(displayId), + ) + } + + override suspend fun onDisplayRemovalAction(instance: PrivacyDotViewController) { + instance.stop() + } + + override val instanceClass = PrivacyDotViewController::class.java +} + +@SysUISingleton +class SingleDisplayPrivacyDotViewControllerStore +@Inject +constructor(defaultController: PrivacyDotViewController) : + PrivacyDotViewControllerStore, + PerDisplayStore<PrivacyDotViewController> by SingleDisplayStore( + defaultInstance = defaultController + ) + +@Module +object PrivacyDotViewControllerStoreModule { + + @Provides + @SysUISingleton + fun store( + singleDisplayLazy: Lazy<SingleDisplayPrivacyDotViewControllerStore>, + multiDisplayLazy: Lazy<MultiDisplayPrivacyDotViewControllerStore>, + ): PrivacyDotViewControllerStore { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + singleDisplayLazy.get() + } + } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(PrivacyDotViewControllerStore::class) + fun storeAsCoreStartable( + multiDisplayLazy: Lazy<MultiDisplayPrivacyDotViewControllerStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + CoreStartable.NOP + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt new file mode 100644 index 000000000000..e471b12c1d58 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt @@ -0,0 +1,119 @@ +/* + * 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.statusbar.data.repository + +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import com.android.systemui.CameraProtectionLoaderImpl +import com.android.systemui.CoreStartable +import com.android.systemui.SysUICutoutProviderImpl +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +/** Provides per display instances of [StatusBarContentInsetsProvider]. */ +interface StatusBarContentInsetsProviderStore : PerDisplayStore<StatusBarContentInsetsProvider> + +@SysUISingleton +class MultiDisplayStatusBarContentInsetsProviderStore +@Inject +constructor( + @Background backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, + private val factory: StatusBarContentInsetsProviderImpl.Factory, + private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val sysUICutoutProviderFactory: SysUICutoutProviderImpl.Factory, + private val cameraProtectionLoaderFactory: CameraProtectionLoaderImpl.Factory, +) : + StatusBarContentInsetsProviderStore, + PerDisplayStoreImpl<StatusBarContentInsetsProvider>( + backgroundApplicationScope, + displayRepository, + ) { + + override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider { + val context = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context + val cameraProtectionLoader = cameraProtectionLoaderFactory.create(context) + return factory + .create( + context, + statusBarConfigurationControllerStore.forDisplay(displayId), + sysUICutoutProviderFactory.create(context, cameraProtectionLoader), + ) + .also { it.start() } + } + + override suspend fun onDisplayRemovalAction(instance: StatusBarContentInsetsProvider) { + instance.stop() + } + + override val instanceClass = StatusBarContentInsetsProvider::class.java +} + +@SysUISingleton +class SingleDisplayStatusBarContentInsetsProviderStore +@Inject +constructor(statusBarContentInsetsProvider: StatusBarContentInsetsProvider) : + StatusBarContentInsetsProviderStore, + PerDisplayStore<StatusBarContentInsetsProvider> by SingleDisplayStore( + defaultInstance = statusBarContentInsetsProvider + ) + +@Module +object StatusBarContentInsetsProviderStoreModule { + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarContentInsetsProviderStore::class) + fun storeAsCoreStartable( + multiDisplayLazy: Lazy<MultiDisplayStatusBarContentInsetsProviderStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + return multiDisplayLazy.get() + } else { + CoreStartable.NOP + } + } + + @Provides + @SysUISingleton + fun store( + singleDisplayLazy: Lazy<SingleDisplayStatusBarContentInsetsProviderStore>, + multiDisplayLazy: Lazy<MultiDisplayStatusBarContentInsetsProviderStore>, + ): StatusBarContentInsetsProviderStore { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + singleDisplayLazy.get() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 0eef8d63c2d1..914cc50a4a3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -34,6 +34,8 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.policy.ConfigurationController @@ -51,7 +53,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.Executor import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Understands how to keep the persistent privacy dot in the corner of the screen in @@ -69,6 +71,11 @@ import kotlinx.coroutines.launch */ interface PrivacyDotViewController { + /** + * Called when the [PrivacyDotViewController] should stop doing any work and clean up if needed. + */ + fun stop() + // Only can be modified on @UiThread var currentViewState: ViewState @@ -137,45 +144,45 @@ constructor( override var showingListener: PrivacyDotViewController.ShowingListener? = null - init { - contentInsetsProvider.addCallback( - object : StatusBarContentInsetsChangedListener { - override fun onStatusBarContentInsetsChanged() { - dlog("onStatusBarContentInsetsChanged: ") - setNewLayoutRects() - } + private val insetsChangedListener = + object : StatusBarContentInsetsChangedListener { + override fun onStatusBarContentInsetsChanged() { + dlog("onStatusBarContentInsetsChanged: ") + setNewLayoutRects() } - ) + } + + private val configurationListener = + object : ConfigurationController.ConfigurationListener { + override fun onLayoutDirectionChanged(isRtl: Boolean) { + uiExecutor?.execute { + // If rtl changed, hide all dots until the next state resolves + setCornerVisibilities(View.INVISIBLE) - configurationController.addCallback( - object : ConfigurationController.ConfigurationListener { - override fun onLayoutDirectionChanged(isRtl: Boolean) { - uiExecutor?.execute { - // If rtl changed, hide all dotes until the next state resolves - setCornerVisibilities(View.INVISIBLE) - - synchronized(this) { - val corner = selectDesignatedCorner(nextViewState.rotation, isRtl) - nextViewState = - nextViewState.copy(layoutRtl = isRtl, designatedCorner = corner) - } + synchronized(this) { + val corner = selectDesignatedCorner(nextViewState.rotation, isRtl) + nextViewState = + nextViewState.copy(layoutRtl = isRtl, designatedCorner = corner) } } } - ) + } - stateController.addCallback( - object : StatusBarStateController.StateListener { - override fun onExpandedChanged(isExpanded: Boolean) { - updateStatusBarState() - } + private val statusBarStateListener = + object : StatusBarStateController.StateListener { + override fun onExpandedChanged(isExpanded: Boolean) { + updateStatusBarState() + } - override fun onStateChanged(newState: Int) { - updateStatusBarState() - } + override fun onStateChanged(newState: Int) { + updateStatusBarState() } - ) + } + init { + contentInsetsProvider.addCallback(insetsChangedListener) + configurationController.addCallback(configurationListener) + stateController.addCallback(statusBarStateListener) scope.launch { shadeInteractor?.isQsExpanded?.collect { isQsExpanded -> dlog("setQsExpanded $isQsExpanded") @@ -184,6 +191,13 @@ constructor( } } + override fun stop() { + StatusBarConnectedDisplays.assertInNewMode() + contentInsetsProvider.removeCallback(insetsChangedListener) + configurationController.removeCallback(configurationListener) + stateController.removeCallback(statusBarStateListener) + } + override fun setUiExecutor(e: DelayableExecutor) { uiExecutor = e } @@ -730,8 +744,12 @@ object PrivacyDotViewControllerModule { factory: PrivacyDotViewControllerImpl.Factory, @Application scope: CoroutineScope, configurationController: ConfigurationController, - contentInsetsProvider: StatusBarContentInsetsProvider, + contentInsetsProviderStore: StatusBarContentInsetsProviderStore, ): PrivacyDotViewController { - return factory.create(scope, configurationController, contentInsetsProvider) + return factory.create( + scope, + configurationController, + contentInsetsProviderStore.defaultDisplay, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 35816c25e976..b28660590ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -33,6 +33,7 @@ import androidx.core.animation.ValueAnimator import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController @@ -453,12 +454,12 @@ object SystemEventChipAnimationControllerModule { factory: SystemEventChipAnimationControllerImpl.Factory, context: Context, statusBarWindowControllerStore: StatusBarWindowControllerStore, - contentInsetsProvider: StatusBarContentInsetsProvider, + contentInsetsProviderStore: StatusBarContentInsetsProviderStore, ): SystemEventChipAnimationController { return factory.create( context, statusBarWindowControllerStore.defaultDisplay, - contentInsetsProvider, + contentInsetsProviderStore.defaultDisplay, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index c6f3d7d21a22..564d52a62cde 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -16,17 +16,19 @@ package com.android.systemui.statusbar.events -import android.annotation.IntDef import androidx.core.animation.Animator import androidx.core.animation.AnimatorSet import androidx.core.animation.PathInterpolator import com.android.systemui.Dumpable +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState import com.android.systemui.statusbar.policy.CallbackController +import kotlinx.coroutines.flow.StateFlow interface SystemStatusAnimationScheduler : CallbackController<SystemStatusAnimationCallback>, Dumpable { - @SystemAnimationState fun getAnimationState(): Int + /** StateFlow holding the current [SystemEventAnimationState] at any time. */ + val animationState: StateFlow<SystemEventAnimationState> fun onStatusEvent(event: StatusEvent) @@ -63,34 +65,6 @@ interface SystemStatusAnimationCallback { } } -/** Animation state IntDef */ -@Retention(AnnotationRetention.SOURCE) -@IntDef( - value = - [ - IDLE, - ANIMATION_QUEUED, - ANIMATING_IN, - RUNNING_CHIP_ANIM, - ANIMATING_OUT, - SHOWING_PERSISTENT_DOT, - ] -) -annotation class SystemAnimationState - -/** No animation is in progress */ -@SystemAnimationState const val IDLE = 0 -/** An animation is queued, and awaiting the debounce period */ -const val ANIMATION_QUEUED = 1 -/** System is animating out, and chip is animating in */ -const val ANIMATING_IN = 2 -/** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */ -const val RUNNING_CHIP_ANIM = 3 -/** Chip is animating away and system is animating back */ -const val ANIMATING_OUT = 4 -/** Chip has animated away, and the persistent dot is showing */ -const val SHOWING_PERSISTENT_DOT = 5 - /** Commonly-needed interpolators can go here */ @JvmField val STATUS_BAR_X_MOVE_OUT = PathInterpolator(0.33f, 0f, 0f, 1f) @JvmField val STATUS_BAR_X_MOVE_IN = PathInterpolator(0f, 0f, 0f, 1f) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt index 5f9e4265c004..6c7b50a0d155 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt @@ -23,6 +23,12 @@ import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.AnimatorSet import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimationQueued +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.ShowingPersistentDot import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.Assert import com.android.systemui.util.time.SystemClock @@ -33,10 +39,11 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withTimeout /** @@ -85,8 +92,8 @@ constructor( */ private var currentlyDisplayedEvent: StatusEvent? = null - /** StateFlow holding the current [SystemAnimationState] at any time. */ - private var animationState = MutableStateFlow(IDLE) + private val _animationState = MutableStateFlow(Idle) + override val animationState = _animationState.asStateFlow() /** True if the persistent privacy dot should be active */ var hasPersistentDot = false @@ -109,24 +116,22 @@ constructor( // Wait for animationState to become ANIMATION_QUEUED and scheduledEvent to be non null. // Once this combination is stable for at least DEBOUNCE_DELAY, then start a chip enter // animation - animationState + _animationState .combine(scheduledEvent) { animationState, scheduledEvent -> Pair(animationState, scheduledEvent) } .debounce(DEBOUNCE_DELAY) .collect { (animationState, event) -> - if (animationState == ANIMATION_QUEUED && event != null) { + if (animationState == AnimationQueued && event != null) { startAnimationLifecycle(event) scheduledEvent.value = null } } } - coroutineScope.launch { animationState.collect { logger?.logAnimationStateUpdate(it) } } + coroutineScope.launch { _animationState.collect { logger?.logAnimationStateUpdate(it) } } } - @SystemAnimationState override fun getAnimationState(): Int = animationState.value - override fun onStatusEvent(event: StatusEvent) { Assert.isMainThread() @@ -146,11 +151,11 @@ constructor( logger?.logScheduleEvent(event) scheduleEvent(event) } else if (currentlyDisplayedEvent?.shouldUpdateFromEvent(event) == true) { - logger?.logUpdateEvent(event, animationState.value) + logger?.logUpdateEvent(event, _animationState.value) currentlyDisplayedEvent?.updateFromEvent(event) if (event.forceVisible) hasPersistentDot = true } else if (scheduledEvent.value?.shouldUpdateFromEvent(event) == true) { - logger?.logUpdateEvent(event, animationState.value) + logger?.logUpdateEvent(event, _animationState.value) scheduledEvent.value?.updateFromEvent(event) } else { logger?.logIgnoreEvent(event) @@ -170,15 +175,15 @@ constructor( // the disappear animation will not animate into a dot but remove the chip entirely hasPersistentDot = false - if (animationState.value == SHOWING_PERSISTENT_DOT) { + if (_animationState.value == ShowingPersistentDot) { // if we are currently showing a persistent dot, hide it and update the animationState notifyHidePersistentDot() if (scheduledEvent.value != null) { - animationState.value = ANIMATION_QUEUED + _animationState.value = AnimationQueued } else { - animationState.value = IDLE + _animationState.value = Idle } - } else if (animationState.value == ANIMATING_OUT) { + } else if (_animationState.value == AnimatingOut) { // if we are currently animating out, hide the dot. The animationState will be updated // once the animation has ended in the onAnimationEnd callback notifyHidePersistentDot() @@ -206,9 +211,9 @@ constructor( cancelCurrentlyDisplayedEvent() return } - if (animationState.value == IDLE) { + if (_animationState.value == Idle) { // If we are in IDLE state, set it to ANIMATION_QUEUED now - animationState.value = ANIMATION_QUEUED + _animationState.value = AnimationQueued } } @@ -223,7 +228,7 @@ constructor( withTimeout(APPEAR_ANIMATION_DURATION) { // wait for animationState to become RUNNING_CHIP_ANIM, then cancel the running // animation job and run the disappear animation immediately - animationState.first { it == RUNNING_CHIP_ANIM } + _animationState.first { it == RunningChipAnim } currentlyRunningAnimationJob?.cancel() runChipDisappearAnimation() } @@ -241,7 +246,7 @@ constructor( if (!event.showAnimation && event.forceVisible) { // If animations are turned off, we'll transition directly to the dot - animationState.value = SHOWING_PERSISTENT_DOT + _animationState.value = ShowingPersistentDot notifyTransitionToPersistentDot(event) return } @@ -277,7 +282,7 @@ constructor( if (hasPersistentDot) { statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(true) } - animationState.value = ANIMATING_IN + _animationState.value = AnimatingIn val animSet = collectStartAnimations() if (animSet.totalDuration > 500) { @@ -289,7 +294,7 @@ constructor( animSet.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { - animationState.value = RUNNING_CHIP_ANIM + _animationState.value = RunningChipAnim } } ) @@ -299,15 +304,15 @@ constructor( private fun runChipDisappearAnimation() { Assert.isMainThread() val animSet2 = collectFinishAnimations() - animationState.value = ANIMATING_OUT + _animationState.value = AnimatingOut animSet2.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { - animationState.value = + _animationState.value = when { - hasPersistentDot -> SHOWING_PERSISTENT_DOT - scheduledEvent.value != null -> ANIMATION_QUEUED - else -> IDLE + hasPersistentDot -> ShowingPersistentDot + scheduledEvent.value != null -> AnimationQueued + else -> Idle } statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(false) } @@ -401,7 +406,7 @@ constructor( pw.println("Scheduled event: ${scheduledEvent.value}") pw.println("Currently displayed event: $currentlyDisplayedEvent") pw.println("Has persistent privacy dot: $hasPersistentDot") - pw.println("Animation state: ${animationState.value}") + pw.println("Animation state: ${_animationState.value}") pw.println("Listeners:") if (listeners.isEmpty()) { pw.println("(none)") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt index 22b0b691ad3b..a1f7a9bf64ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt @@ -3,15 +3,14 @@ package com.android.systemui.statusbar.events import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState import javax.inject.Inject /** Logs for the SystemStatusAnimationScheduler. */ @SysUISingleton class SystemStatusAnimationSchedulerLogger @Inject -constructor( - @SystemStatusAnimationSchedulerLog private val logBuffer: LogBuffer, -) { +constructor(@SystemStatusAnimationSchedulerLog private val logBuffer: LogBuffer) { fun logScheduleEvent(event: StatusEvent) { logBuffer.log( @@ -23,11 +22,11 @@ constructor( bool1 = event.forceVisible bool2 = event.showAnimation }, - { "Scheduling event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" } + { "Scheduling event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }, ) } - fun logUpdateEvent(event: StatusEvent, @SystemAnimationState animationState: Int) { + fun logUpdateEvent(event: StatusEvent, animationState: SystemEventAnimationState) { logBuffer.log( TAG, LogLevel.DEBUG, @@ -36,12 +35,12 @@ constructor( int1 = event.priority bool1 = event.forceVisible bool2 = event.showAnimation - int2 = animationState + str2 = animationState.name }, { "Updating current event from: $str1(forceVisible=$bool1, priority=$int1, " + - "showAnimation=$bool2), animationState=${animationState.name()}" - } + "showAnimation=$bool2), animationState=$str2" + }, ) } @@ -55,7 +54,7 @@ constructor( bool1 = event.forceVisible bool2 = event.showAnimation }, - { "Ignore event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" } + { "Ignore event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }, ) } @@ -67,26 +66,14 @@ constructor( logBuffer.log(TAG, LogLevel.DEBUG, "Transition to persistent dot callback invoked") } - fun logAnimationStateUpdate(@SystemAnimationState animationState: Int) { + fun logAnimationStateUpdate(animationState: SystemEventAnimationState) { logBuffer.log( TAG, LogLevel.DEBUG, - { int1 = animationState }, - { "AnimationState update: ${int1.name()}" } + { str1 = animationState.name }, + { "AnimationState update: $str1" }, ) - animationState.name() } - - private fun @receiver:SystemAnimationState Int.name() = - when (this) { - IDLE -> "IDLE" - ANIMATION_QUEUED -> "ANIMATION_QUEUED" - ANIMATING_IN -> "ANIMATING_IN" - RUNNING_CHIP_ANIM -> "RUNNING_CHIP_ANIM" - ANIMATING_OUT -> "ANIMATING_OUT" - SHOWING_PERSISTENT_DOT -> "SHOWING_PERSISTENT_DOT" - else -> "UNKNOWN_ANIMATION_STATE" - } } private const val TAG = "SystemStatusAnimationSchedulerLog" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/shared/model/SystemEventAnimationState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/shared/model/SystemEventAnimationState.kt new file mode 100644 index 000000000000..2446b81e006c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/shared/model/SystemEventAnimationState.kt @@ -0,0 +1,35 @@ +/* + * 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.statusbar.events.shared.model + +/** Direct representation of the system event animation scheduler's current state */ +enum class SystemEventAnimationState { + /** No animation is in progress */ + Idle, + /** An animation is queued, and awaiting the debounce period */ + AnimationQueued, + /** System is animating out, and chip is animating in */ + AnimatingIn, + /** + * Chip has animated in and is awaiting exit animation, and optionally playing its own animation + */ + RunningChipAnim, + /** Chip is animating away and system is animating back */ + AnimatingOut, + /** Chip has animated away, and the persistent dot is showing */ + ShowingPersistentDot, +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 682a9fff2994..77ec65bfad6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -29,6 +29,7 @@ import com.android.internal.widget.MessagingLayout import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -141,7 +142,7 @@ class ConversationNotificationManager @Inject constructor( bindEventManager: BindEventManager, - private val context: Context, + @ShadeDisplayAware private val context: Context, private val notifCollection: CommonNotifCollection, @Main private val mainHandler: Handler ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt index 231a0b0b21cb..9fe4a5499ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.kt @@ -32,13 +32,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow interface NotificationActivityStarter { /** Called when the user clicks on the notification bubble icon. */ - fun onNotificationBubbleIconClicked(entry: NotificationEntry?) + fun onNotificationBubbleIconClicked(entry: NotificationEntry) /** Called when the user clicks on the surface of a notification. */ - fun onNotificationClicked(entry: NotificationEntry?, row: ExpandableNotificationRow?) + fun onNotificationClicked(entry: NotificationEntry, row: ExpandableNotificationRow) /** Called when the user clicks on a button in the notification guts which fires an intent. */ - fun startNotificationGutsIntent(intent: Intent?, appUid: Int, row: ExpandableNotificationRow?) + fun startNotificationGutsIntent(intent: Intent, appUid: Int, row: ExpandableNotificationRow) /** * Called when the user clicks "Manage" or "History" in the Shade. Prefer using @@ -56,7 +56,7 @@ interface NotificationActivityStarter { fun startSettingsIntent(view: View, intentInfo: SettingsIntent) /** Called when the user succeed to drop notification to proper target view. */ - fun onDragSuccess(entry: NotificationEntry?) + fun onDragSuccess(entry: NotificationEntry) val isCollapsingToShowActivityOverLockscreen: Boolean get() = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 22c537cb93f4..ea515e0c2cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -49,7 +49,7 @@ import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class NotificationWakeUpCoordinator diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt new file mode 100644 index 000000000000..48a8c01e7c47 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt @@ -0,0 +1,46 @@ +/* + * 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.statusbar.notification.collection + +import com.android.internal.statusbar.NotificationVisibility +import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats + +/** + * A holder class for a [NotificationEntry] and an associated [DismissedByUserStats], used by + * [NotifCollection] for handling dismissal. + */ +data class EntryWithDismissStats(val entry: NotificationEntry, val stats: DismissedByUserStats) { + /** + * Creates deep a copy of this object, but with the entry, key and rank updated to correspond to + * the given entry. + */ + fun copyForEntry(newEntry: NotificationEntry) = + EntryWithDismissStats( + entry = newEntry, + stats = + DismissedByUserStats( + stats.dismissalSurface, + stats.dismissalSentiment, + NotificationVisibility.obtain( + newEntry.key, + newEntry.ranking.rank, + stats.notificationVisibility.count, + /* visible= */ false, + ), + ), + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 7b3a93a4a094..cf9ee619b734 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -39,6 +39,7 @@ import static android.service.notification.NotificationListenerService.REASON_TI import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED; import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED; +import static com.android.systemui.Flags.notificationsDismissPrunedSummaries; import static com.android.systemui.statusbar.notification.NotificationUtils.logKey; import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED; import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED; @@ -62,7 +63,6 @@ import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -111,6 +111,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -273,15 +274,19 @@ public class NotifCollection implements Dumpable, PipelineDumpable { * Dismisses multiple notifications on behalf of the user. */ public void dismissNotifications( - List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) { + List<EntryWithDismissStats> entriesToDismiss) { Assert.isMainThread(); checkForReentrantCall(); + if (notificationsDismissPrunedSummaries()) { + entriesToDismiss = includeSummariesToDismiss(entriesToDismiss); + } + final int entryCount = entriesToDismiss.size(); final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>(); for (int i = 0; i < entriesToDismiss.size(); i++) { - NotificationEntry entry = entriesToDismiss.get(i).first; - DismissedByUserStats stats = entriesToDismiss.get(i).second; + NotificationEntry entry = entriesToDismiss.get(i).getEntry(); + DismissedByUserStats stats = entriesToDismiss.get(i).getStats(); requireNonNull(stats); NotificationEntry storedEntry = mNotificationSet.get(entry.getKey()); @@ -336,13 +341,32 @@ public class NotifCollection implements Dumpable, PipelineDumpable { dispatchEventsAndRebuildList("dismissNotifications"); } + private List<EntryWithDismissStats> includeSummariesToDismiss( + List<EntryWithDismissStats> entriesToDismiss) { + final HashSet<NotificationEntry> entriesSet = new HashSet<>(entriesToDismiss.size()); + for (EntryWithDismissStats entryToStats : entriesToDismiss) { + entriesSet.add(entryToStats.getEntry()); + } + + final List<EntryWithDismissStats> entriesPlusSummaries = + new ArrayList<>(entriesToDismiss.size() + 1); + for (EntryWithDismissStats entryToStats : entriesToDismiss) { + entriesPlusSummaries.add(entryToStats); + NotificationEntry summary = fetchSummaryToDismiss(entryToStats.getEntry()); + if (summary != null && !entriesSet.contains(summary)) { + entriesPlusSummaries.add(entryToStats.copyForEntry(summary)); + } + } + return entriesPlusSummaries; + } + /** * Dismisses a single notification on behalf of the user. */ public void dismissNotification( NotificationEntry entry, @NonNull DismissedByUserStats stats) { - dismissNotifications(List.of(new Pair<>(entry, stats))); + dismissNotifications(List.of(new EntryWithDismissStats(entry, stats))); } /** @@ -1062,6 +1086,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable { } } + @Nullable + private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) { + if (isOnlyChildInGroup(entry)) { + String group = entry.getSbn().getGroupKey(); + NotificationEntry summary = getGroupSummary(group); + if (summary != null && isDismissable(summary)) return summary; + } + return null; + } + /** A single method interface that callers can pass in when registering future dismissals */ public interface DismissedByUserStatsCreator { DismissedByUserStats createDismissedByUserStats(NotificationEntry entry); @@ -1092,16 +1126,6 @@ public class NotifCollection implements Dumpable, PipelineDumpable { + ">"; } - @Nullable - private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) { - if (isOnlyChildInGroup(entry)) { - String group = entry.getSbn().getGroupKey(); - NotificationEntry summary = getGroupSummary(group); - if (summary != null && isDismissable(summary)) return summary; - } - return null; - } - /** called when the entry has been removed from the collection */ public void onSystemServerCancel(@CancellationReason int cancellationReason) { Assert.isMainThread(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt index 2ee1dffd14f3..958001625a07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt @@ -35,6 +35,9 @@ import java.util.concurrent.atomic.AtomicInteger * This cache is safe for multithreaded usage, and is recommended for objects that take a while to * resolve (such as drawables, or things that require binder calls). As such, [getOrFetch] is * recommended to be run on a background thread, while [purge] can be done from any thread. + * + * Important: This cache does NOT have a maximum size, cleaning it up (via [purge]) is the + * responsibility of the caller, to avoid keeping things in memory unnecessarily. */ @SuppressLint("DumpableNotRegistered") // this will be dumped by container classes class NotifCollectionCache<V>( @@ -151,7 +154,7 @@ class NotifCollectionCache<V>( * purge((c)); // deletes a from the cache and marks b for deletion, etc. * ``` */ - fun purge(wantedKeys: List<String>) { + fun purge(wantedKeys: Collection<String>) { for ((key, entry) in cache) { if (key in wantedKeys) { entry.resetLives() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationUiFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationUiFlag.kt new file mode 100644 index 000000000000..9020b7628d31 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationUiFlag.kt @@ -0,0 +1,51 @@ +/* + * 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.statusbar.notification.collection + +import android.app.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION */ +@Suppress("NOTHING_TO_INLINE") +object NotificationClassificationUiFlag { + const val FLAG_NAME = Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Are sections sorted by time? */ + @JvmStatic + inline val isEnabled + get() = Flags.notificationClassificationUi() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index e74ed8d27ec2..205c42f3c2f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -586,7 +586,8 @@ public final class NotificationEntry extends ListEntry { } Bundle extras = mSbn.getNotification().extras; Parcelable[] replyTexts = - extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + RemoteInputHistoryItem.class); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt index 496fb83c1cf9..1a521d767438 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt @@ -1,100 +1,141 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.notification.collection.coordinator import android.util.Log - -import com.android.systemui.log.dagger.NotificationHeadsUpLog import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.dagger.NotificationHeadsUpLog import javax.inject.Inject private const val TAG = "HeadsUpCoordinator" -class HeadsUpCoordinatorLogger constructor( - private val buffer: LogBuffer, - private val verbose: Boolean, -) { +class HeadsUpCoordinatorLogger(private val buffer: LogBuffer, private val verbose: Boolean) { @Inject - constructor(@NotificationHeadsUpLog buffer: LogBuffer) : - this(buffer, Log.isLoggable(TAG, Log.VERBOSE)) + constructor( + @NotificationHeadsUpLog buffer: LogBuffer + ) : this(buffer, Log.isLoggable(TAG, Log.VERBOSE)) fun logPostedEntryWillEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) { if (!verbose) return - buffer.log(TAG, LogLevel.VERBOSE, { - str1 = posted.key - str2 = reason - bool1 = posted.shouldHeadsUpEver - bool2 = posted.shouldHeadsUpAgain - }, { - "will evaluate posted entry $str1:" + + buffer.log( + TAG, + LogLevel.VERBOSE, + { + str1 = posted.key + str2 = reason + bool1 = posted.shouldHeadsUpEver + bool2 = posted.shouldHeadsUpAgain + }, + { + "will evaluate posted entry $str1:" + " reason=$str2 shouldHeadsUpEver=$bool1 shouldHeadsUpAgain=$bool2" - }) + }, + ) } fun logPostedEntryWillNotEvaluate(posted: HeadsUpCoordinator.PostedEntry, reason: String) { if (!verbose) return - buffer.log(TAG, LogLevel.VERBOSE, { - str1 = posted.key - str2 = reason - }, { - "will not evaluate posted entry $str1: reason=$str2" - }) + buffer.log( + TAG, + LogLevel.VERBOSE, + { + str1 = posted.key + str2 = reason + }, + { "will not evaluate posted entry $str1: reason=$str2" }, + ) } fun logEvaluatingGroups(numGroups: Int) { if (!verbose) return - buffer.log(TAG, LogLevel.VERBOSE, { - int1 = numGroups - }, { - "evaluating groups for alert transfer: $int1" - }) + buffer.log( + TAG, + LogLevel.VERBOSE, + { int1 = numGroups }, + { "evaluating groups for alert transfer: $int1" }, + ) } fun logEvaluatingGroup(groupKey: String, numPostedEntries: Int, logicalGroupSize: Int) { if (!verbose) return - buffer.log(TAG, LogLevel.VERBOSE, { - str1 = groupKey - int1 = numPostedEntries - int2 = logicalGroupSize - }, { - "evaluating group for alert transfer: $str1" + + buffer.log( + TAG, + LogLevel.VERBOSE, + { + str1 = groupKey + int1 = numPostedEntries + int2 = logicalGroupSize + }, + { + "evaluating group for alert transfer: $str1" + " numPostedEntries=$int1 logicalGroupSize=$int2" - }) + }, + ) } fun logEntryUpdatedByRanking(key: String, shouldHun: Boolean, reason: String) { - buffer.log(TAG, LogLevel.DEBUG, { - str1 = key - bool1 = shouldHun - str2 = reason - }, { - "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1 because $str2" - }) + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = key + bool1 = shouldHun + str2 = reason + }, + { + "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1 because $str2" + }, + ) } fun logEntryUpdatedToFullScreen(key: String, reason: String) { - buffer.log(TAG, LogLevel.DEBUG, { - str1 = key - str2 = reason - }, { - "updating entry to launch full screen intent: $str1 because $str2" - }) + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = key + str2 = reason + }, + { "updating entry to launch full screen intent: $str1 because $str2" }, + ) } fun logEntryDisqualifiedFromFullScreen(key: String, reason: String) { - buffer.log(TAG, LogLevel.DEBUG, { - str1 = key - str2 = reason - }, { - "updated entry no longer qualifies for full screen intent: $str1 because $str2" - }) + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = key + str2 = reason + }, + { "updated entry no longer qualifies for full screen intent: $str1 because $str2" }, + ) } fun logSummaryMarkedInterrupted(summaryKey: String, childKey: String) { - buffer.log(TAG, LogLevel.DEBUG, { - str1 = summaryKey - str2 = childKey - }, { - "marked group summary as interrupted: $str1 for alert transfer to child: $str2" - }) + buffer.log( + TAG, + LogLevel.DEBUG, + { + str1 = summaryKey + str2 = childKey + }, + { "marked group summary as interrupted: $str1 for alert transfer to child: $str2" }, + ) } } 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 4e63b920a73d..2fded34a56a0 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 @@ -50,7 +50,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * If the setting is enabled, this will track seen notifications and ensure that they only show in 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 cf1329c6b564..366704e27b9b 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 @@ -52,7 +52,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.yield /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 9b075a650b48..f75163d2662b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -27,6 +27,7 @@ import android.os.Trace; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -49,13 +50,18 @@ import com.android.systemui.statusbar.notification.collection.render.NotifViewBa import com.android.systemui.statusbar.notification.collection.render.NotifViewController; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager; import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager.NotifInflationErrorListener; +import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.inject.Inject; @@ -104,6 +110,8 @@ public class PreparationCoordinator implements Coordinator { /** How long we can delay a group while waiting for all children to inflate */ private final long mMaxGroupInflationDelay; private final BindEventManagerImpl mBindEventManager; + private final AppIconProvider mAppIconProvider; + private final NotificationIconStyleProvider mNotificationIconStyleProvider; @Inject public PreparationCoordinator( @@ -113,7 +121,9 @@ public class PreparationCoordinator implements Coordinator { NotifViewBarn viewBarn, NotifUiAdjustmentProvider adjustmentProvider, IStatusBarService service, - BindEventManagerImpl bindEventManager) { + BindEventManagerImpl bindEventManager, + AppIconProvider appIconProvider, + NotificationIconStyleProvider notificationIconStyleProvider) { this( logger, notifInflater, @@ -122,6 +132,8 @@ public class PreparationCoordinator implements Coordinator { adjustmentProvider, service, bindEventManager, + appIconProvider, + notificationIconStyleProvider, CHILD_BIND_CUTOFF, MAX_GROUP_INFLATION_DELAY); } @@ -135,6 +147,8 @@ public class PreparationCoordinator implements Coordinator { NotifUiAdjustmentProvider adjustmentProvider, IStatusBarService service, BindEventManagerImpl bindEventManager, + AppIconProvider appIconProvider, + NotificationIconStyleProvider notificationIconStyleProvider, int childBindCutoff, long maxGroupInflationDelay) { mLogger = logger; @@ -146,6 +160,8 @@ public class PreparationCoordinator implements Coordinator { mChildBindCutoff = childBindCutoff; mMaxGroupInflationDelay = maxGroupInflationDelay; mBindEventManager = bindEventManager; + mAppIconProvider = appIconProvider; + mNotificationIconStyleProvider = notificationIconStyleProvider; } @Override @@ -155,6 +171,9 @@ public class PreparationCoordinator implements Coordinator { () -> mNotifInflatingFilter.invalidateList("adjustmentProviderChanged")); pipeline.addCollectionListener(mNotifCollectionListener); + if (android.app.Flags.notificationsRedesignAppIcons()) { + pipeline.addOnBeforeTransformGroupsListener(this::purgeCaches); + } // Inflate after grouping/sorting since that affects what views to inflate. pipeline.addOnBeforeFinalizeFilterListener(this::inflateAllRequiredViews); pipeline.addFinalizeFilter(mNotifInflationErrorFilter); @@ -260,6 +279,29 @@ public class PreparationCoordinator implements Coordinator { } }; + private void purgeCaches(Collection<ListEntry> entries) { + Set<String> wantedPackages = getPackages(entries); + mAppIconProvider.purgeCache(wantedPackages); + mNotificationIconStyleProvider.purgeCache(wantedPackages); + } + + /** + * Get all app packages present in {@param entries}. + */ + private static @NonNull Set<String> getPackages(Collection<ListEntry> entries) { + Set<String> packages = new HashSet<>(); + for (ListEntry entry : entries) { + NotificationEntry notificationEntry = entry.getRepresentativeEntry(); + if (notificationEntry == null) { + Log.wtf(TAG, "notification entry " + entry.getKey() + + " has no representative entry"); + continue; + } + packages.add(notificationEntry.getSbn().getPackageName()); + } + return packages; + } + private void inflateAllRequiredViews(List<ListEntry> entries) { for (int i = 0, size = entries.size(); i < size; i++) { ListEntry entry = entries.get(i); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt index df694bb67684..de868d45e64f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.content.Context import com.android.systemui.res.R +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline @@ -33,7 +34,7 @@ import javax.inject.Inject */ @CoordinatorScope class RowAppearanceCoordinator @Inject internal constructor( - context: Context, + @ShadeDisplayAware context: Context, private var mAssistantFeedbackController: AssistantFeedbackController, private var mSectionStyleProvider: SectionStyleProvider ) : Coordinator { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index 1e0e597ad3e8..ef7b1c3d562e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -46,7 +46,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @Module(includes = [PrivateSensitiveContentCoordinatorModule::class]) interface SensitiveContentCoordinatorModule diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt index b8a959440312..db778b801dbc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Compile import com.android.app.tracing.traceSection +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject /** @@ -39,7 +40,7 @@ import javax.inject.Inject */ @CoordinatorScope class ViewConfigCoordinator @Inject internal constructor( - private val mConfigurationController: ConfigurationController, + @ShadeDisplayAware private val mConfigurationController: ConfigurationController, private val mLockscreenUserManager: NotificationLockscreenUserManager, private val mGutsManager: NotificationGutsManager, private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 9d5d7a19916a..e6d22b07f3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -37,6 +37,7 @@ import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -86,7 +87,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { @Inject public NotificationRowBinderImpl( - Context context, + @ShadeDisplayAware Context context, NotificationMessagingUtil notificationMessagingUtil, NotificationRemoteInputManager notificationRemoteInputManager, NotificationLockscreenUserManager notificationLockscreenUserManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 193586679d23..cab4c1c88b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -27,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumper import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.app.tracing.traceSection +import com.android.systemui.shade.ShadeDisplayAware import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -36,7 +37,7 @@ import dagger.assisted.AssistedInject * currently populate the notification shade. */ class ShadeViewManager @AssistedInject constructor( - context: Context, + @ShadeDisplayAware context: Context, @Assisted listContainer: NotificationListContainer, @Assisted private val stackController: NotifStackController, mediaContainerController: MediaContainerController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt index d36412cf193e..7d374b051b76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationSettingsRepositoryModule.kt @@ -32,7 +32,7 @@ import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @Module(includes = [SecureSettingsRepositoryModule::class, SystemSettingsRepositoryModule::class]) object NotificationSettingsRepositoryModule { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt index 697a6ce52ba9..5900fb034b4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel @@ -78,7 +78,7 @@ constructor( /** The notifications that are promoted and ongoing. Sorted by priority order. */ val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> = - if (StatusBarNotifChips.isEnabled) { + if (StatusBarRonChips.isEnabled) { // TODO(b/364653005): Filter all the notifications down to just the promoted ones. // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow // instead of being separate. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt index 7f1b04358546..9c5f9a44ad0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewbinder/EmptyShadeViewBinder.kt @@ -21,7 +21,7 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object EmptyShadeViewBinder { suspend fun bind( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index 5a616dfd1f63..e5ce25d5144e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -40,6 +40,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import com.android.settingslib.Utils; +import com.android.systemui.Flags; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; @@ -59,6 +60,9 @@ public class FooterView extends StackScrollerDecorView { private FooterViewButton mClearAllButton; private FooterViewButton mManageOrHistoryButton; + // The settings & history buttons replace the single manage/history button in the redesign + private FooterViewButton mSettingsButton; + private FooterViewButton mHistoryButton; private boolean mShouldBeHidden; private boolean mShowHistory; // String cache, for performance reasons. @@ -269,7 +273,12 @@ public class FooterView extends StackScrollerDecorView { } super.onFinishInflate(); mClearAllButton = (FooterViewButton) findSecondaryView(); - mManageOrHistoryButton = findViewById(R.id.manage_text); + if (Flags.notificationsRedesignFooterView()) { + mSettingsButton = findViewById(R.id.settings_button); + mHistoryButton = findViewById(R.id.history_button); + } else { + mManageOrHistoryButton = findViewById(R.id.manage_text); + } mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer); if (!FooterViewRefactor.isEnabled()) { updateResources(); @@ -342,8 +351,10 @@ public class FooterView extends StackScrollerDecorView { updateClearAllButtonText(); updateClearAllButtonDescription(); - updateManageOrHistoryButtonText(); - updateManageOrHistoryButtonDescription(); + if (!Flags.notificationsRedesignFooterView()) { + updateManageOrHistoryButtonText(); + updateManageOrHistoryButtonDescription(); + } updateMessageString(); updateMessageIcon(); @@ -420,8 +431,16 @@ public class FooterView extends StackScrollerDecorView { } mClearAllButton.setBackground(clearAllBg); mClearAllButton.setTextColor(onSurface); - mManageOrHistoryButton.setBackground(manageBg); - mManageOrHistoryButton.setTextColor(onSurface); + if (Flags.notificationsRedesignFooterView()) { + mSettingsButton.setBackground(manageBg); + mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface)); + + mHistoryButton.setBackground(manageBg); + mHistoryButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface)); + } else { + mManageOrHistoryButton.setBackground(manageBg); + mManageOrHistoryButton.setTextColor(onSurface); + } mSeenNotifsFooterTextView.setTextColor(onSurface); mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface)); ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt index 22bec5a43230..ddd9cdd5e306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.footer.ui.viewbinder import android.view.View import androidx.lifecycle.lifecycleScope +import com.android.systemui.Flags import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix @@ -28,7 +29,7 @@ import com.android.systemui.util.ui.stopAnimating import com.android.systemui.util.ui.value import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [FooterView] to its [view model][FooterViewModel]. */ object FooterViewBinder { @@ -63,14 +64,16 @@ object FooterViewBinder { notificationActivityStarter: NotificationActivityStarter, ) = coroutineScope { launch { bindClearAllButton(footer, viewModel, clearAllNotifications) } - launch { - bindManageOrHistoryButton( - footer, - viewModel, - launchNotificationSettings, - launchNotificationHistory, - notificationActivityStarter, - ) + if (!Flags.notificationsRedesignFooterView()) { + launch { + bindManageOrHistoryButton( + footer, + viewModel, + launchNotificationSettings, + launchNotificationHistory, + notificationActivityStarter, + ) + } } launch { bindMessage(footer, viewModel) } } 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 dc6ab4126337..db804835f260 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 @@ -44,7 +44,7 @@ import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt index 38489f928146..663588c8f8c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.ui.SystemBarUtilsState import javax.inject.Inject import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */ class NotificationIconContainerAlwaysOnDisplayViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt index 3599f1f51966..f0f529e2c615 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.ui.SystemBarUtilsState import javax.inject.Inject import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */ class NotificationIconContainerStatusBarViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index c8d6abe18ac3..063fe45cd199 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -54,7 +54,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a view-model to a [NotificationIconContainer]. */ object NotificationIconContainerViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt index d4466f8771a6..71cddc99b564 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -29,6 +29,7 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision @@ -72,7 +73,7 @@ constructor( private val systemSettings: SystemSettings, private val packageManager: PackageManager, private val bubbles: Optional<Bubbles>, - private val context: Context, + @ShadeDisplayAware private val context: Context, private val notificationManager: NotificationManager, private val settingsInteractor: NotificationSettingsInteractor ) : VisualInterruptionDecisionProvider { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt index 614719aed53d..e233deffe42f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt @@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.graphics.ImageLoader +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Initial @@ -44,7 +45,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext private const val TAG = "BigPicImageLoader" @@ -61,7 +62,7 @@ private const val FREE_IMAGE_DELAY_MS = 3000L class BigPictureIconManager @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, private val imageLoader: ImageLoader, private val statsManager: BigPictureStatsManager, @Application private val scope: CoroutineScope, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt index 24b5cf1aa33b..98d704c75d33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt @@ -16,31 +16,64 @@ package com.android.systemui.statusbar.notification.row.icon +import android.annotation.WorkerThread import android.app.ActivityManager import android.app.Flags import android.content.Context import android.content.pm.PackageManager.NameNotFoundException import android.graphics.Color -import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable +import android.os.UserHandle import android.util.Log import com.android.internal.R import com.android.launcher3.icons.BaseIconFactory +import com.android.launcher3.icons.BaseIconFactory.IconOptions +import com.android.launcher3.util.UserIconInfo +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.shade.ShadeDisplayAware +import com.android.systemui.statusbar.notification.collection.NotifCollectionCache +import com.android.systemui.util.asIndenting +import com.android.systemui.util.withIncreasedIndent import dagger.Module import dagger.Provides +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider /** A provider used to cache and fetch app icons used by notifications. */ interface AppIconProvider { + /** + * Loads the icon corresponding to [packageName] into cache, or fetches it from there if already + * present. This should only be called from the background. + */ @Throws(NameNotFoundException::class) - fun getOrFetchAppIcon(packageName: String, context: Context): Drawable + @WorkerThread + fun getOrFetchAppIcon( + packageName: String, + context: Context, + withWorkProfileBadge: Boolean = false, + ): Drawable + + /** + * Mark all the entries in the cache that are NOT in [wantedPackages] to be cleared. If they're + * still not needed on the next call of this method (made after a timeout of 1s, in case they + * happen more frequently than that), they will be purged. This can be done from any thread. + */ + fun purgeCache(wantedPackages: Collection<String>) } @SysUISingleton -class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider { +class AppIconProviderImpl +@Inject +constructor(@ShadeDisplayAware private val sysuiContext: Context, dumpManager: DumpManager) : + AppIconProvider, Dumpable { + init { + dumpManager.registerNormalDumpable(TAG, this) + } + private val iconFactory: BaseIconFactory get() { val isLowRam = ActivityManager.isLowRamDeviceStatic() @@ -53,13 +86,74 @@ class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize) } - override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { - val icon = context.packageManager.getApplicationIcon(packageName) - return BitmapDrawable( - context.resources, - iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE), + private val cache = NotifCollectionCache<Drawable>() + + override fun getOrFetchAppIcon( + packageName: String, + context: Context, + withWorkProfileBadge: Boolean, + ): Drawable { + // Add a suffix to distinguish the app installed on the work profile, since the icon will + // be different. + val key = packageName + if (withWorkProfileBadge) WORK_SUFFIX else "" + + return cache.getOrFetch(key) { fetchAppIcon(packageName, context, withWorkProfileBadge) } + } + + @WorkerThread + private fun fetchAppIcon( + packageName: String, + context: Context, + withWorkProfileBadge: Boolean, + ): Drawable { + val pm = context.packageManager + val icon = pm.getApplicationInfo(packageName, 0).loadUnbadgedIcon(pm) + + val options = + IconOptions().apply { + setUser(userIconInfo(context, withWorkProfileBadge)) + setBitmapGenerationMode(BaseIconFactory.MODE_HARDWARE) + // This color is not used since we're not showing the themed icons. We're just + // setting it so that the icon factory doesn't try to extract colors from our bitmap + // (since it won't work, given it's a hardware bitmap). + setExtractedColor(Color.BLUE) + } + val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options) + return badgedIcon.newIcon(sysuiContext) + } + + private fun userIconInfo(context: Context, withWorkProfileBadge: Boolean): UserIconInfo { + val userId = context.userId + return UserIconInfo( + UserHandle.of(userId), + if (withWorkProfileBadge) UserIconInfo.TYPE_WORK else UserIconInfo.TYPE_MAIN, ) } + + override fun purgeCache(wantedPackages: Collection<String>) { + // We don't know from the packages if it's the work profile app or not, so let's just keep + // both if they're present in the cache. + cache.purge(wantedPackages.flatMap { listOf(it, "$it$WORK_SUFFIX") }) + } + + override fun dump(pwOrig: PrintWriter, args: Array<out String>) { + val pw = pwOrig.asIndenting() + + pw.println("cache information:") + pw.withIncreasedIndent { cache.dump(pw, args) } + + val iconFactory = iconFactory + pw.println("icon factory information:") + pw.withIncreasedIndent { + pw.println("fullResIconDpi = ${iconFactory.fullResIconDpi}") + pw.println("iconSize = ${iconFactory.iconBitmapSize}") + } + } + + companion object { + const val TAG = "AppIconProviderImpl" + const val WORK_SUFFIX = "|WORK" + } } class NoOpIconProvider : AppIconProvider { @@ -67,10 +161,18 @@ class NoOpIconProvider : AppIconProvider { const val TAG = "NoOpIconProvider" } - override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + override fun getOrFetchAppIcon( + packageName: String, + context: Context, + withWorkProfileBadge: Boolean, + ): Drawable { Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") return ColorDrawable(Color.WHITE) } + + override fun purgeCache(wantedPackages: Collection<String>) { + Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt index 165c1a7803a9..7177a7bd473a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt @@ -20,11 +20,18 @@ import android.annotation.WorkerThread import android.app.Flags import android.content.Context import android.content.pm.ApplicationInfo +import android.os.UserManager import android.service.notification.StatusBarNotification import android.util.Log +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.collection.NotifCollectionCache +import com.android.systemui.util.asIndenting +import com.android.systemui.util.withIncreasedIndent import dagger.Module import dagger.Provides +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Provider @@ -33,15 +40,43 @@ import javax.inject.Provider * notifications. */ interface NotificationIconStyleProvider { + /** + * Determines whether the [notification] should display the app icon instead of the small icon. + * This can result in a binder call, and therefore should only be called from the background. + */ @WorkerThread fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean + + /** + * Whether the [notification] is coming from a work profile app, and therefore should display + * the briefcase badge. + */ + fun shouldShowWorkProfileBadge(notification: StatusBarNotification, context: Context): Boolean + + /** + * Mark all the entries in the cache that are NOT in [wantedPackages] to be cleared. If they're + * still not needed on the next call of this method (made after a timeout of 1s, in case they + * happen more frequently than that), they will be purged. This can be done from any thread. + */ + fun purgeCache(wantedPackages: Collection<String>) } @SysUISingleton -class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider { +class NotificationIconStyleProviderImpl +@Inject +constructor(private val userManager: UserManager, dumpManager: DumpManager) : + NotificationIconStyleProvider, Dumpable { + init { + dumpManager.registerNormalDumpable(TAG, this) + } + + private val cache = NotifCollectionCache<Boolean>() + override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean { val packageContext = notification.getPackageContext(context) - return !belongsToHeadlessSystemApp(packageContext) + return cache.getOrFetch(notification.packageName) { + !belongsToHeadlessSystemApp(packageContext) + } } @WorkerThread @@ -62,6 +97,29 @@ class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIcon return false } } + + override fun shouldShowWorkProfileBadge( + notification: StatusBarNotification, + context: Context, + ): Boolean { + val packageContext = notification.getPackageContext(context) + // UserManager already caches this, so we don't need to. + return userManager.isManagedProfile(packageContext.userId) + } + + override fun purgeCache(wantedPackages: Collection<String>) { + cache.purge(wantedPackages) + } + + override fun dump(pwOrig: PrintWriter, args: Array<out String>) { + val pw = pwOrig.asIndenting() + pw.println("cache information:") + pw.withIncreasedIndent { cache.dump(pw, args) } + } + + companion object { + const val TAG = "NotificationIconStyleProviderImpl" + } } class NoOpIconStyleProvider : NotificationIconStyleProvider { @@ -73,6 +131,18 @@ class NoOpIconStyleProvider : NotificationIconStyleProvider { Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") return true } + + override fun shouldShowWorkProfileBadge( + notification: StatusBarNotification, + context: Context, + ): Boolean { + Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") + return false + } + + override fun purgeCache(wantedPackages: Collection<String>) { + Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.") + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt index 7b85bfdfb197..64fdf6fc2708 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt @@ -50,6 +50,7 @@ constructor( NotificationRowIconView(context, attrs).also { view -> view.setIconProvider(createIconProvider(row, context)) } + else -> null } } @@ -61,13 +62,19 @@ constructor( val sbn = row.entry.sbn return object : NotificationIconProvider { override fun shouldShowAppIcon(): Boolean { - val shouldShowAppIcon = iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context) + val shouldShowAppIcon = iconStyleProvider.shouldShowAppIcon(sbn, context) row.setIsShowingAppIcon(shouldShowAppIcon) return shouldShowAppIcon } override fun getAppIcon(): Drawable { - return appIconProvider.getOrFetchAppIcon(sbn.packageName, context) + val withWorkProfileBadge = + iconStyleProvider.shouldShowWorkProfileBadge(sbn, context) + return appIconProvider.getOrFetchAppIcon( + sbn.packageName, + context, + withWorkProfileBadge, + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt index 2527af87728e..f0b5c3667962 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt @@ -28,7 +28,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.notification.row.ActivatableNotificationView import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds an [ActivatableNotificationView] to its [view model][ActivatableNotificationViewModel]. */ object ActivatableNotificationViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt index b42b2b29d944..d7a50a00586a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/NotificationViewFlipperBinder.kt @@ -22,7 +22,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.notification.row.ui.viewmodel.NotificationViewFlipperViewModel import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [NotificationViewFlipper] to its [view model][NotificationViewFlipperViewModel]. */ object NotificationViewFlipperBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 15dc1157aa69..0352a304a5c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -24,7 +24,7 @@ import com.android.systemui.statusbar.notification.row.ui.viewbinder.Activatable import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */ object NotificationShelfViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 7441c7058d7b..c9a0010c0de7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -20,6 +20,7 @@ import android.util.Log import android.view.View import com.android.internal.annotations.VisibleForTesting import com.android.systemui.media.controls.ui.controller.KeyguardMediaController +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.SourceType import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag @@ -41,7 +42,7 @@ import javax.inject.Inject class NotificationSectionsManager @Inject internal constructor( - private val configurationController: ConfigurationController, + @ShadeDisplayAware private val configurationController: ConfigurationController, private val keyguardMediaController: KeyguardMediaController, private val sectionsFeatureManager: NotificationSectionsFeatureManager, private val mediaContainerController: MediaContainerController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ec1dc0a77118..1d4916c6cd82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1200,7 +1200,6 @@ public class NotificationStackScrollLayout if (!SceneContainerFlag.isEnabled()) { setMaxLayoutHeight(getHeight()); updateContentHeight(); - mWallpaperInteractor.setNotificationStackAbsoluteBottom(mContentHeight); } clampScrollPosition(); requestChildrenUpdate(); @@ -1278,7 +1277,6 @@ public class NotificationStackScrollLayout if (mAmbientState.getStackTop() != stackTop) { mAmbientState.setStackTop(stackTop); onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending()); - mWallpaperInteractor.setNotificationStackAbsoluteBottom((int) stackTop); } } @@ -1574,7 +1572,7 @@ public class NotificationStackScrollLayout if (mMaxDisplayedNotifications != -1) { // The stack intrinsic height already contains the correct value when there is a limit // in the max number of notifications (e.g. as in keyguard). - height = mIntrinsicContentHeight; + height = mScrollViewFields.getIntrinsicStackHeight(); } else { height = Math.max(0f, mAmbientState.getStackCutoff() - mAmbientState.getStackTop()); } @@ -2610,7 +2608,7 @@ public class NotificationStackScrollLayout } @VisibleForTesting - void updateStackHeight() { + void updateIntrinsicStackHeight() { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; @@ -2621,8 +2619,11 @@ public class NotificationStackScrollLayout mMaxDisplayedNotifications, shelfIntrinsicHeight ); - mIntrinsicContentHeight = notificationsHeight; - final int fullStackHeight = notificationsHeight + footerIntrinsicHeight + mBottomPadding; + // When there is a limit in the max number of notifications, we never display the footer. + final int fullStackHeight = mMaxDisplayedNotifications != -1 + ? notificationsHeight + : notificationsHeight + footerIntrinsicHeight + mBottomPadding; + if (mScrollViewFields.getIntrinsicStackHeight() != fullStackHeight) { mScrollViewFields.setIntrinsicStackHeight(fullStackHeight); notifyStackHeightChangedListeners(); @@ -2631,7 +2632,7 @@ public class NotificationStackScrollLayout private void updateContentHeight() { if (SceneContainerFlag.isEnabled()) { - updateStackHeight(); + updateIntrinsicStackHeight(); return; } @@ -2645,6 +2646,7 @@ public class NotificationStackScrollLayout // The topPadding can be bigger than the regular padding when qs is expanded, in that // state the maxPanelHeight and the contentHeight should be bigger + mContentHeight = (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding); updateScrollability(); @@ -5348,7 +5350,12 @@ public class NotificationStackScrollLayout public void setMaxDisplayedNotifications(int maxDisplayedNotifications) { if (mMaxDisplayedNotifications != maxDisplayedNotifications) { mMaxDisplayedNotifications = maxDisplayedNotifications; - updateContentHeight(); + if (SceneContainerFlag.isEnabled()) { + updateIntrinsicStackHeight(); + updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction()); + } else { + updateContentHeight(); + } notifyHeightChangeListener(mShelf); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 04974b4353f4..400654698d5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -43,7 +43,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.Log; -import android.util.Pair; import android.util.Property; import android.view.Display; import android.view.MotionEvent; @@ -105,6 +104,7 @@ import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNot import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -1777,12 +1777,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { mNotifCollection.dismissAllNotifications( mLockscreenUserManager.getCurrentUserId()); } else { - final List<Pair<NotificationEntry, DismissedByUserStats>> + final List<EntryWithDismissStats> entriesWithRowsDismissedFromShade = new ArrayList<>(); for (ExpandableNotificationRow row : viewsToRemove) { final NotificationEntry entry = row.getEntry(); entriesWithRowsDismissedFromShade.add( - new Pair<>(entry, getDismissedByUserStats(entry))); + new EntryWithDismissStats(entry, getDismissedByUserStats(entry))); } mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 109f0ae41d76..b251b078826b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.keyguard.BouncerPanelExpansionCalculator; +import com.android.systemui.Flags; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; @@ -467,10 +468,20 @@ public class StackScrollAlgorithm { if (v instanceof EmptyShadeView) { emptyShadeVisible = true; } - if (v instanceof FooterView) { + if (v instanceof FooterView footerView) { if (emptyShadeVisible || notGoneIndex == 0) { // if the empty shade is visible or the footer is the first visible // view, we're in a transitory state so let's leave the footer alone. + if (Flags.notificationsFooterVisibilityFix() + && !SceneContainerFlag.isEnabled()) { + // ...except for the hidden state, to prevent it from flashing on + // the screen (this piece is copied from updateChild, and is not + // necessary in flexiglass). + if (footerView.shouldBeHidden() + || !ambientState.isShadeExpanded()) { + footerView.getViewState().hidden = true; + } + } continue; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt index 5d3747628e7e..4894ad3f1192 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt @@ -18,17 +18,20 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context -import com.android.systemui.common.ui.data.repository.ConfigurationRepository +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.LargeScreenHeaderHelper +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.policy.SplitShadeStateController import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -36,17 +39,17 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart /** Encapsulates business-logic specifically related to the shared notification stack container. */ +@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) @SysUISingleton class SharedNotificationContainerInteractor @Inject constructor( - configurationRepository: ConfigurationRepository, - private val context: Context, + @ShadeDisplayAware private val context: Context, private val splitShadeStateController: Lazy<SplitShadeStateController>, private val shadeInteractor: Lazy<ShadeInteractor>, + configurationInteractor: ConfigurationInteractor, keyguardInteractor: KeyguardInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>, @@ -59,9 +62,6 @@ constructor( /** An internal modification was made to notifications */ val notificationStackChanged = _notificationStackChanged.debounce(20L) - private val configurationChangeEvents = - configurationRepository.onAnyConfigurationChange.onStart { emit(Unit) } - /* Warning: Even though the value it emits only contains the split shade status, this flow must * emit a value whenever the configuration *or* the split shade status changes. Adding a * distinctUntilChanged() to this would cause configurationBasedDimensions to miss configuration @@ -69,13 +69,14 @@ constructor( */ private val dimensionsUpdateEventsWithShouldUseSplitShade: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { - combine(configurationChangeEvents, shadeInteractor.get().isShadeLayoutWide) { - _, - isShadeLayoutWide -> + combine( + configurationInteractor.onAnyConfigurationChange, + shadeInteractor.get().isShadeLayoutWide, + ) { _, isShadeLayoutWide -> isShadeLayoutWide } } else { - configurationChangeEvents.map { + configurationInteractor.onAnyConfigurationChange.map { splitShadeStateController.get().shouldUseSplitNotificationShade(context.resources) } } @@ -115,11 +116,6 @@ constructor( isUdfpsSupported || !ambientIndicationVisible } - val isSplitShadeEnabled: Flow<Boolean> = - configurationBasedDimensions - .map { dimens: ConfigurationBasedDimensions -> dimens.useSplitShade } - .distinctUntilChanged() - /** Top position (without translation) of the shared container. */ fun setTopPosition(top: Float) { _topPosition.value = top diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt index 534e5c3fda87..53749ff24394 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt @@ -34,7 +34,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext @VisibleForTesting const val UNKNOWN_RANK = -1 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt index c2bc9ba83293..55a2813ab9e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Binds a [NotificationStackScrollLayoutController] to its [view model][NotificationListViewModel]. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index ebae235f88d6..fd19f1ff634e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -22,6 +22,7 @@ import androidx.lifecycle.lifecycleScope import com.android.app.tracing.TraceUtils.traceAsync import com.android.internal.logging.MetricsLogger import com.android.internal.logging.nano.MetricsProto +import com.android.systemui.Flags import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo import com.android.systemui.dagger.qualifiers.Background @@ -69,7 +70,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */ class NotificationListViewBinder @@ -145,7 +146,9 @@ constructor( // The footer needs to be re-inflated every time the theme or the font size changes. configuration .inflateLayout<FooterView>( - R.layout.status_bar_notification_footer, + if (Flags.notificationsRedesignFooterView()) + R.layout.status_bar_notification_footer_redesign + else R.layout.status_bar_notification_footer, parentView, attachToRoot = false, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index b22143f03b44..ce89d786c350 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -18,10 +18,12 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.scene.shared.flag.SceneContainerFlag @@ -35,7 +37,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds the shared notification container to its view-model. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -48,8 +50,19 @@ constructor( private val notificationScrollViewBinder: NotificationScrollViewBinder, private val communalSettingsInteractor: CommunalSettingsInteractor, @Main private val mainImmediateDispatcher: CoroutineDispatcher, + val keyguardInteractor: KeyguardInteractor, ) { + private val calculateMaxNotifications: (Float, Boolean) -> Int = { space, extraShelfSpace -> + val shelfHeight = controller.getShelfHeight().toFloat() + notificationStackSizeCalculator.computeMaxKeyguardNotifications( + controller.view, + space, + if (extraShelfSpace) shelfHeight else 0f, + shelfHeight, + ) + } + fun bind( view: SharedNotificationContainer, viewModel: SharedNotificationContainerViewModel, @@ -107,17 +120,9 @@ constructor( } launch { - viewModel - .getMaxNotifications { space, extraShelfSpace -> - val shelfHeight = controller.getShelfHeight().toFloat() - notificationStackSizeCalculator.computeMaxKeyguardNotifications( - controller.getView(), - space, - if (extraShelfSpace) shelfHeight else 0f, - shelfHeight, - ) - } - .collect { controller.setMaxDisplayedNotifications(it) } + viewModel.getMaxNotifications(calculateMaxNotifications).collect { + controller.setMaxDisplayedNotifications(it) + } } if (!SceneContainerFlag.isEnabled) { @@ -136,6 +141,30 @@ constructor( } } + if (!SceneContainerFlag.isEnabled) { + if (Flags.magicPortraitWallpapers()) { + launch { + viewModel + .getNotificationStackAbsoluteBottom( + calculateMaxNotifications = calculateMaxNotifications, + calculateHeight = { maxNotifications -> + notificationStackSizeCalculator.computeHeight( + maxNotifs = maxNotifications, + shelfHeight = controller.getShelfHeight().toFloat(), + stack = controller.view, + ) + }, + controller.getShelfHeight().toFloat(), + ) + .collect { bottom -> + keyguardInteractor.setNotificationStackAbsoluteBottom( + bottom + ) + } + } + } + } + launch { viewModel.translationX.collect { x -> controller.translationX = x } } launch { 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 574ca3ffe5b6..56b335648138 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 @@ -104,7 +104,7 @@ constructor( 1f } else if ( change.isTransitioningBetween(Scenes.Gone, Scenes.Shade) || - change.isTransitioning(from = Scenes.Gone, to = Scenes.Lockscreen) + change.isTransitioning(from = Scenes.Shade, to = Scenes.Lockscreen) ) { shadeExpansion } else if (change.isTransitioningBetween(Scenes.Gone, Scenes.QuickSettings)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt index a8ce47cf2dd3..49cd7cb4fb8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * ViewModel used by the Notification placeholders inside the scene container to update the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 878ae91391e9..eeebac06206c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -221,13 +221,15 @@ constructor( /** If the user is visually on one of the unoccluded lockscreen states. */ val isOnLockscreen: Flow<Boolean> = anyOf( - keyguardTransitionInteractor.isFinishedIn(AOD), - keyguardTransitionInteractor.isFinishedIn(DOZING), - keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER), - keyguardTransitionInteractor.isFinishedIn( - scene = Scenes.Bouncer, - stateWithoutSceneContainer = PRIMARY_BOUNCER, - ), + keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f }, + keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f }, + keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f }, + keyguardTransitionInteractor + .transitionValue( + scene = Scenes.Bouncer, + stateWithoutSceneContainer = PRIMARY_BOUNCER, + ) + .map { it > 0f }, keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f }, ) .flowName("isOnLockscreen") @@ -484,19 +486,28 @@ constructor( } fun keyguardAlpha(viewState: ViewStateAccessor, scope: CoroutineScope): Flow<Float> { - // Transitions are not (yet) authoritative for NSSL; they still rely on StatusBarState to - // help determine when the device has fully moved to GONE or OCCLUDED state. Once SHADE - // state has been set, let shade alpha take over - val isKeyguardNotVisible = - combine( + val isKeyguardOccluded = + keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f } + + val isKeyguardNotVisibleInState = + if (SceneContainerFlag.isEnabled) { + isKeyguardOccluded + } else { anyOf( - keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f }, + isKeyguardOccluded, keyguardTransitionInteractor .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE) .map { it == 1f }, - ), - keyguardInteractor.statusBarState, - ) { isKeyguardNotVisibleInState, statusBarState -> + ) + } + + // Transitions are not (yet) authoritative for NSSL; they still rely on StatusBarState to + // help determine when the device has fully moved to GONE or OCCLUDED state. Once SHADE + // state has been set, let shade alpha take over + val isKeyguardNotVisible = + combine(isKeyguardNotVisibleInState, keyguardInteractor.statusBarState) { + isKeyguardNotVisibleInState, + statusBarState -> isKeyguardNotVisibleInState && statusBarState == SHADE } @@ -663,6 +674,36 @@ constructor( .dumpWhileCollecting("maxNotifications") } + /** + * Wallpaper needs the absolute bottom of notification stack to avoid occlusion + * + * @param calculateMaxNotifications is required by getMaxNotifications as calculateSpace by + * calling computeMaxKeyguardNotifications in NotificationStackSizeCalculator + * @param calculateHeight is calling computeHeight in NotificationStackSizeCalculator The edge + * case is that when maxNotifications is 0, we won't take shelfHeight into account + */ + fun getNotificationStackAbsoluteBottom( + calculateMaxNotifications: (Float, Boolean) -> Int, + calculateHeight: (Int) -> Float, + shelfHeight: Float, + ): Flow<Float> { + SceneContainerFlag.assertInLegacyMode() + + return combine( + getMaxNotifications(calculateMaxNotifications).map { + val height = calculateHeight(it) + if (it == 0) { + height - shelfHeight + } else { + height + } + }, + bounds.map { it.top }, + ) { height, top -> + top + height + } + } + fun notificationStackChanged() { interactor.notificationStackChanged() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt index e2e5c5970ff5..feb74098f071 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class HeadsUpNotificationViewBinder @Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index afa5a7802559..65663fd3f19c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -199,6 +199,7 @@ import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.data.model.StatusBarMode; @@ -645,6 +646,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { NavigationBarController navigationBarController, AccessibilityFloatingMenuController accessibilityFloatingMenuController, Lazy<AssistManager> assistManagerLazy, + // TODO: b/374267505 - Decouple the config change needed for shade window classes from + // the one for other windows. ConfigurationController configurationController, NotificationShadeWindowController notificationShadeWindowController, Lazy<NotificationShadeWindowViewController> notificationShadeWindowViewControllerLazy, @@ -808,7 +811,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStartingSurfaceOptional = startingSurfaceOptional; mDreamManager = dreamManager; lockscreenShadeTransitionController.setCentralSurfaces(this); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); } mScreenOffAnimationController = screenOffAnimationController; @@ -896,7 +899,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWallpaperSupported = mWallpaperManager.isWallpaperSupported(); RegisterStatusBarResult result = null; - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { try { result = mBarService.registerStatusBar(mCommandQueue); } catch (RemoteException ex) { @@ -909,9 +912,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) { mStatusBarModeRepository.getDefaultDisplay().showTransient(); } @@ -1010,9 +1013,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mAccessibilityFloatingMenuController.init(); - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { // set the initial view visibility int disabledFlags1 = result.mDisabledFlags1; int disabledFlags2 = result.mDisabledFlags2; @@ -1179,9 +1182,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWallpaperController.setRootView(getNotificationShadeWindowView()); mDemoModeController.addCallback(mDemoModeCallback); - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator. - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { mJavaAdapter.alwaysCollectFlow( mStatusBarModeRepository.getDefaultDisplay().isTransientShown(), this::onTransientShownChanged); @@ -1198,9 +1201,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator); mWakeUpCoordinator.onPanelExpansionChanged(currentState); - // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, all this logic will be done in // StatusBarOrchestrator. - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { // Allow plugins to reference DarkIconDispatcher and StatusBarStateController mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class); mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class); @@ -1222,6 +1225,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { setBouncerShowingForStatusBarComponents(mBouncerShowing); checkBarModes(); }); + } + if (!StatusBarSimpleFragment.isEnabled() && !StatusBarConnectedDisplays.isEnabled()) { // When the flag is on, we register the fragment as a core startable and this is not // needed mStatusBarInitializer.initializeStatusBar(); @@ -1229,16 +1234,16 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView()); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { createNavigationBar(result); } mAmbientIndicationContainer = getNotificationShadeWindowView().findViewById( R.id.ambient_indication_container); - // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, all this logic will be done in // StatusBarOrchestrator. - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { mAutoHideController.setStatusBar( new AutoHideUiElement() { @Override @@ -1498,14 +1503,14 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { * @param state2 disable2 flags */ protected void setUpDisableFlags(int state1, int state2) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarConnectedDisplays.assertInLegacyMode(); mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */); } // TODO(b/117478341): This was left such that CarStatusBar can override this method. // Try to remove this. protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarConnectedDisplays.assertInLegacyMode(); mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result); } @@ -1718,9 +1723,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void checkBarModes() { if (mDemoModeController.isInDemoMode()) return; - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator. - if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) { + if (!StatusBarConnectedDisplays.isEnabled() && mStatusBarTransitions != null) { checkBarMode( mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(), mStatusBarWindowState, @@ -1751,9 +1756,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } private void finishBarAnimations() { - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator. - if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) { + if (!StatusBarConnectedDisplays.isEnabled() && mStatusBarTransitions != null) { mStatusBarTransitions.finishAnimations(); } mNavigationBarController.finishBarAnimations(mDisplayId); @@ -1795,14 +1800,14 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { pw.print(" mStatusBarWindowState="); pw.println(windowStateToString(mStatusBarWindowState)); } pw.print(" mDozing="); pw.println(mDozing); pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { CentralSurfaces.dumpBarTransitions( pw, "PhoneStatusBarTransitions", mStatusBarTransitions); } @@ -1878,9 +1883,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private void createAndAddWindows(@Nullable RegisterStatusBarResult result) { makeStatusBarView(result); mNotificationShadeWindowController.attach(); - // When the StatusBarSimpleFragment flag is enabled, this logic will be done in + // When the StatusBarConnectedDisplays flag is enabled, this logic will be done in // StatusBarOrchestrator - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarConnectedDisplays.isEnabled()) { mStatusBarWindowControllerStore.getDefaultDisplay().attach(); } } @@ -1972,6 +1977,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { * meantime, just update the things that we know change. */ void updateResources() { + // TODO: b/374267505 - we shouldn't propagate this from here. Each class should be + // listening at the correct configuration change. For example, shade window classes should + // be listening at @ShadeDisplayAware configurations (as it can be on a different display. + // Update the quick setting tiles if (mQSPanelController != null) { mQSPanelController.updateResources(); @@ -2507,7 +2516,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { int importance = bouncerShowing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : IMPORTANT_FOR_ACCESSIBILITY_AUTO; - if (!StatusBarSimpleFragment.isEnabled() && mPhoneStatusBarViewController != null) { + if (!StatusBarConnectedDisplays.isEnabled() && mPhoneStatusBarViewController != null) { mPhoneStatusBarViewController.setImportantForAccessibility(importance); } mShadeSurface.setImportantForAccessibility(importance); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt new file mode 100644 index 000000000000..3fd46fc484a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationForwarder.kt @@ -0,0 +1,31 @@ +/* + * 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.statusbar.phone + +import android.content.res.Configuration + +/** + * Used to forward a configuration change to other components. + * + * This is commonly used to propagate configs to [ConfigurationController]. Note that there could be + * different configuration forwarder, for example each display, window or group of classes (e.g. + * shade window classes). + */ +interface ConfigurationForwarder { + /** Should be called when a new configuration is received. */ + fun onConfigurationChanged(newConfiguration: Configuration) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index a34ac2e11c2e..3242149eed92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -47,7 +47,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @SysUISingleton class KeyguardBypassController @Inject constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index cd59d4ebfd73..be2fb68ab88d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -58,6 +58,7 @@ import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore; import com.android.systemui.statusbar.disableflags.DisableStateTracker; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -300,7 +301,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat KeyguardStatusBarViewModel keyguardStatusBarViewModel, BiometricUnlockController biometricUnlockController, SysuiStatusBarStateController statusBarStateController, - StatusBarContentInsetsProvider statusBarContentInsetsProvider, + StatusBarContentInsetsProviderStore statusBarContentInsetsProviderStore, UserManager userManager, StatusBarUserChipViewModel userChipViewModel, SecureSettings secureSettings, @@ -327,7 +328,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mKeyguardStatusBarViewModel = keyguardStatusBarViewModel; mBiometricUnlockController = biometricUnlockController; mStatusBarStateController = statusBarStateController; - mInsetsProvider = statusBarContentInsetsProvider; + mInsetsProvider = statusBarContentInsetsProviderStore.getDefaultDisplay(); mUserManager = userManager; mStatusBarUserChipViewModel = userChipViewModel; mSecureSettings = secureSettings; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index ff7c14308fcb..746d6a75a567 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -38,6 +38,7 @@ import com.android.systemui.shade.ShadeLogger import com.android.systemui.shade.ShadeViewController import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowStateController @@ -333,7 +334,7 @@ private constructor( private val configurationController: ConfigurationController, private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory, private val darkIconDispatcher: DarkIconDispatcher, - private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider, + private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore, ) { fun create(view: PhoneStatusBarView): PhoneStatusBarViewController { val statusBarMoveFromCenterAnimationController = @@ -359,7 +360,7 @@ private constructor( configurationController, statusOverlayHoverListenerFactory, darkIconDispatcher, - statusBarContentInsetsProvider, + statusBarContentInsetsProviderStore.defaultDisplay, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index c6f6bd90fce6..d991b1df45fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -24,6 +24,7 @@ import android.graphics.Point import android.graphics.Rect import android.util.LruCache import android.util.Pair +import android.view.Display.DEFAULT_DISPLAY import android.view.DisplayCutout import android.view.Surface import androidx.annotation.VisibleForTesting @@ -37,7 +38,7 @@ import com.android.systemui.SysUICutoutProvider import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.statusbar.commandline.CommandRegistry -import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl.CacheKey +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE @@ -70,6 +71,17 @@ interface StatusBarContentInsetsProvider : CallbackController<StatusBarContentInsetsChangedListener> { /** + * Called when the [StatusBarContentInsetsProvider] should start doing its work and allocate its + * resources. + */ + fun start() + + /** + * Called when the [StatusBarContentInsetsProvider] should stop and do any required clean up. + */ + fun stop() + + /** * Some views may need to care about whether or not the current top display cutout is located in * the corner rather than somewhere in the center. In the case of a corner cutout, the status * bar area is contiguous. @@ -157,10 +169,15 @@ constructor( context.resources.getBoolean(R.bool.config_enablePrivacyDot) } - init { + private val nameSuffix = + if (context.displayId == DEFAULT_DISPLAY) "" else context.displayId.toString() + private val dumpableName = TAG + nameSuffix + private val commandName = StatusBarInsetsCommand.NAME + nameSuffix + + override fun start() { configurationController.addCallback(this) - dumpManager.registerDumpable(TAG, this) - commandRegistry.registerCommand(StatusBarInsetsCommand.NAME) { + dumpManager.registerDumpable(dumpableName, this) + commandRegistry.registerCommand(commandName) { StatusBarInsetsCommand( object : StatusBarInsetsCommand.Callback { override fun onExecute( @@ -174,6 +191,13 @@ constructor( } } + override fun stop() { + StatusBarConnectedDisplays.assertInNewMode() + configurationController.removeCallback(this) + dumpManager.unregisterDumpable(dumpableName) + commandRegistry.unregisterCommand(commandName) + } + override fun addCallback(listener: StatusBarContentInsetsChangedListener) { listeners.add(listener) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt index fa6d2797a37e..98da5b19976d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt @@ -11,7 +11,7 @@ import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.util.concurrency.DelayableExecutor import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import java.io.PrintWriter import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index f7fea7b0d334..92b609e87a90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -56,6 +56,7 @@ import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; import com.android.systemui.Flags; +import com.android.systemui.animation.back.FlingOnBackAnimationCallback; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; @@ -236,9 +237,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } }; - private final OnBackAnimationCallback mOnBackInvokedCallback = new OnBackAnimationCallback() { + private final OnBackAnimationCallback mOnBackInvokedCallback = + new FlingOnBackAnimationCallback() { @Override - public void onBackInvoked() { + public void onBackInvokedCompat() { if (DEBUG) { Log.d(TAG, "onBackInvokedCallback() called, invoking onBackPressed()"); } @@ -249,21 +251,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override - public void onBackProgressed(BackEvent event) { + public void onBackProgressedCompat(@NonNull BackEvent event) { if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { mPrimaryBouncerView.getDelegate().getBackCallback().onBackProgressed(event); } } @Override - public void onBackCancelled() { + public void onBackCancelledCompat() { if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { mPrimaryBouncerView.getDelegate().getBackCallback().onBackCancelled(); } } @Override - public void onBackStarted(BackEvent event) { + public void onBackStartedCompat(@NonNull BackEvent event) { if (shouldPlayBackAnimation() && mPrimaryBouncerView.getDelegate() != null) { mPrimaryBouncerView.getDelegate().getBackCallback().onBackStarted(event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 93db2db918b0..af98311c937f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -20,7 +20,6 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import static android.service.notification.NotificationListenerService.REASON_CLICK; import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions; -import static com.android.systemui.util.kotlin.NullabilityKt.expectNotNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -231,8 +230,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param entry notification that bubble icon was clicked */ @Override - public void onNotificationBubbleIconClicked(NotificationEntry entry) { - expectNotNull(TAG, "entry", entry); + public void onNotificationBubbleIconClicked(@NonNull NotificationEntry entry) { Runnable action = () -> { mBubblesManagerOptional.ifPresent(bubblesManager -> bubblesManager.onUserChangedBubble(entry, !entry.isBubble())); @@ -258,9 +256,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param row row for that notification */ @Override - public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) { - expectNotNull(TAG, "entry", entry); - expectNotNull(TAG, "row", row); + public void onNotificationClicked(@NonNull NotificationEntry entry, + @NonNull ExpandableNotificationRow row) { mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(), mKeyguardStateController.isVisible(), mNotificationShadeWindowController.getPanelExpanded()); @@ -442,8 +439,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit * @param entry notification entry that is dropped. */ @Override - public void onDragSuccess(NotificationEntry entry) { - expectNotNull(TAG, "entry", entry); + public void onDragSuccess(@NonNull NotificationEntry entry) { // this method is not responsible for intent sending. // will focus follow operation only after drag-and-drop that notification. final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true); @@ -534,10 +530,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } @Override - public void startNotificationGutsIntent(final Intent intent, final int appUid, - ExpandableNotificationRow row) { - expectNotNull(TAG, "intent", intent); - expectNotNull(TAG, "row", row); + public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid, + @NonNull ExpandableNotificationRow row) { boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt index c40822d71952..c341bd3466ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class StatusOverlayHoverListenerFactory @Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt index 31c213434abf..7d36873db10d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt @@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** A dialog shown as a bottom sheet. */ class SystemUIBottomSheetDialog diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 92d0ebecf8d8..99f25bd00839 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -78,8 +78,11 @@ interface StatusBarPhoneModule { return if (StatusBarConnectedDisplays.isEnabled) { // Will be started through MultiDisplayStatusBarStarter CoreStartable.NOP - } else { + } else if (StatusBarSimpleFragment.isEnabled) { defaultInitializerLazy.get() + } else { + // Will be started through CentralSurfaces + CoreStartable.NOP } } @@ -119,24 +122,6 @@ interface StatusBarPhoneModule { @Provides @SysUISingleton @IntoMap - @ClassKey(StatusBarOrchestrator::class) - fun orchestratorCoreStartable( - @Default orchestratorLazy: Lazy<StatusBarOrchestrator> - ): CoreStartable { - return if (StatusBarConnectedDisplays.isEnabled) { - // Will be started through MultiDisplayStatusBarStarter - CoreStartable.NOP - } else if (StatusBarSimpleFragment.isEnabled) { - orchestratorLazy.get() - } else { - // Will be started through CentralSurfacesImpl - CoreStartable.NOP - } - } - - @Provides - @SysUISingleton - @IntoMap @ClassKey(MultiDisplayStatusBarStarter::class) fun multiDisplayStarter( multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 37c8c637c804..f85785f5475f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -56,7 +56,8 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; +import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips; +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; @@ -656,7 +657,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } boolean showSecondaryOngoingActivityChip = Flags.statusBarScreenSharingChips() - && StatusBarNotifChips.isEnabled() + && StatusBarRonChips.isEnabled() && mHasSecondaryOngoingActivity; return new StatusBarVisibilityModel( @@ -698,7 +699,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean showSecondaryOngoingActivityChip = // Secondary chips are only supported when RONs are enabled. - StatusBarNotifChips.isEnabled() + StatusBarRonChips.isEnabled() && visibilityModel.getShowSecondaryOngoingActivityChip() && !disableNotifications; if (showSecondaryOngoingActivityChip) { @@ -711,8 +712,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private boolean shouldHideStatusBar() { StatusBarSimpleFragment.assertInLegacyMode(); + boolean isDefaultDisplay = getContext().getDisplayId() == Display.DEFAULT_DISPLAY; + boolean shouldHideForCurrentDisplay = + !StatusBarConnectedDisplays.isEnabled() || isDefaultDisplay; if (!mShadeExpansionStateManager.isClosed() - && mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded()) { + && mPanelExpansionInteractor.shouldHideStatusBarIconsWhenExpanded() + //TODO(b/373310629): for now the shade only shows on the main display. + // Remove this once there is a display aware API for the shade. + && shouldHideForCurrentDisplay) { return true; } @@ -825,7 +832,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showSecondaryOngoingActivityChip(boolean animate) { - StatusBarNotifChips.assertInNewMode(); + StatusBarRonChips.assertInNewMode(); StatusBarSimpleFragment.assertInLegacyMode(); animateShow(mSecondaryOngoingActivityChip, animate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java index f6f8adb851e9..45c53b05d478 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Module; @@ -128,9 +129,8 @@ public interface HomeStatusBarModule { @HomeStatusBarScope static PhoneStatusBarTransitions providePhoneStatusBarTransitions( @RootView PhoneStatusBarView view, - StatusBarWindowControllerStore statusBarWindowControllerStore) { - return new PhoneStatusBarTransitions( - view, statusBarWindowControllerStore.getDefaultDisplay().getBackgroundView()); + StatusBarWindowController statusBarWindowController) { + return new PhoneStatusBarTransitions(view, statusBarWindowController.getBackgroundView()); } /** */ @@ -156,4 +156,12 @@ public interface HomeStatusBarModule { return store.forDisplay(displayId); } + /** */ + @Provides + @HomeStatusBarScope + static StatusBarWindowController provideWindowController( + @DisplaySpecific int displayId, StatusBarWindowControllerStore store) { + return store.forDisplay(displayId); + } + } 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 3cf8c3f48409..aac2cd1755d0 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 @@ -58,7 +58,7 @@ import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** A controller to handle the ongoing call chip in the collapsed status bar. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt index af58999d9ddf..428d16a1a175 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigCoreStartable.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Core startable which configures the [CarrierConfigRepository] to listen for updates for the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt index 3a79f3fb3573..bb9310f5369a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt @@ -51,7 +51,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** This repository vends out data based on demo mode commands */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt index 5a49f8eb8f3b..30cc2c5da994 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt @@ -27,7 +27,7 @@ import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * This class is intended to provide a context to collect on the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 5f08afdee74a..31d349eb4cca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -48,7 +48,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBin import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch private data class Colors( @ColorInt val tint: Int, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconsBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconsBinder.kt index fc0ba131c457..f24f7d0bd339 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconsBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconsBinder.kt @@ -23,7 +23,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.phone.ui.IconManager import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object MobileIconsBinder { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt index 081e1015e26e..5c80fda72373 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt @@ -22,7 +22,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel import com.android.systemui.util.AutoMarqueeTextView -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object ShadeCarrierBinder { /** Binds the view to the view-model, continuing to update the former based on the latter */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index 694a5e529ec4..2efbfbbf78c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * View model for describing the system's current mobile cellular connections. The result is a list diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt index 6ad295e82645..d557bbf306a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt @@ -24,7 +24,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** A satellite repository that represents the latest satellite values sent via demo mode. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt index 470abe63b568..7686338fd9eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt @@ -66,7 +66,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/binder/DeviceBasedSatelliteIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/binder/DeviceBasedSatelliteIconBinder.kt index 59ac5f29b66c..fd9037e182b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/binder/DeviceBasedSatelliteIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/binder/DeviceBasedSatelliteIconBinder.kt @@ -23,7 +23,7 @@ import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModel import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding import com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarIconView -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch object DeviceBasedSatelliteIconBinder { fun bind( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt index 8d7b57db4125..473f95629b4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt @@ -27,7 +27,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.core.StatusBarSimpleFragment @@ -36,7 +35,7 @@ import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel]. @@ -85,7 +84,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde } } - if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) { + if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) launch { @@ -121,7 +120,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde } } - if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) { + if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) val secondaryChipView: View = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt index 189dc40d275f..79757ca429d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt @@ -22,7 +22,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel import java.util.function.Consumer import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Binds an [InternetTileModel] flow to a consumer for the internet tile to apply to its qs state diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index a21cc22ad03f..a472318a1e86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.composable +import android.view.Display import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -43,7 +44,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarVie import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Factory to simplify the dependency management for [StatusBarRoot] */ class StatusBarRootFactory @@ -152,8 +153,14 @@ fun StatusBarRoot( phoneStatusBarView.requireViewById<NotificationIconContainer>( R.id.notificationIcons ) - scope.launch { - notificationIconsBinder.bindWhileAttached(notificationIconContainer) + + // TODO(b/369337701): implement notification icons for all displays. + // Currently if we try to bind for all displays, there is a crash, because the + // same notification icon view can't have multiple parents. + if (context.displayId == Display.DEFAULT_DISPLAY) { + scope.launch { + notificationIconsBinder.bindWhileAttached(notificationIconContainer) + } } // This binder handles everything else diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconView.kt index c663c37fec98..90a2e2047d16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarIconView.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarV import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Simple single-icon view that is bound to bindable_status_bar_icon.xml */ class SingleBindableStatusBarIconView( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt index f4bb1a34b05f..5d879ddc4ddd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Demo-able wifi repository to support SystemUI demo mode commands. */ class DemoWifiRepository diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt index 2800c9404c4b..64447fd72557 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt @@ -28,7 +28,7 @@ import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWi import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel import javax.inject.Inject -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * This class serves as a bridge between the old UI classes and the new data pipeline. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt index 95124905e576..fd93c6bd09b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Binds a wifi icon in the status bar to its view-model. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java index cec77c12a40b..1bb4e8c66ef1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java @@ -16,16 +16,15 @@ package com.android.systemui.statusbar.policy; import android.content.res.Configuration; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; /** * Common listener for configuration or subsets of configuration changes (like density or * font scaling), providing easy static dependence on these events. */ -public interface ConfigurationController extends CallbackController<ConfigurationListener> { - - /** Alert controller of a change in the configuration. */ - void onConfigurationChanged(Configuration newConfiguration); +public interface ConfigurationController extends CallbackController<ConfigurationListener>, + ConfigurationForwarder { /** Alert controller of a change in between light and dark themes. */ void notifyThemeChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt index 32b476b07d90..b966cb17ad17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt @@ -24,7 +24,7 @@ import com.android.systemui.modes.shared.ModesUi import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt index 96717c7542d5..5c1a4279ea90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt @@ -24,7 +24,7 @@ import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index b81af86b0779..c7bd5a1bb9a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.NetworkControllerImpl; import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.ConfigurationForwarder; import com.android.systemui.statusbar.policy.BatteryControllerLogger; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; @@ -186,6 +187,13 @@ public interface StatusBarPolicyModule { DevicePostureControllerImpl devicePostureControllerImpl); /** */ + @Binds + @SysUISingleton + @GlobalConfig + ConfigurationForwarder provideGlobalConfigurationForwarder( + @GlobalConfig ConfigurationController configurationController); + + /** */ @Provides @SysUISingleton @GlobalConfig diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt index 6988e211855b..39ec676ed5cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt @@ -23,7 +23,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.phone.KeyguardStatusBarView import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds [KeyguardStatusBarViewModel] to [KeyguardStatusBarView]. */ object KeyguardStatusBarViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt index ae0e76f01faa..584cd3b00a57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt @@ -23,6 +23,7 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import java.util.Optional /** Encapsulates all logic for the status bar window state management. */ @@ -82,6 +83,7 @@ interface StatusBarWindowController { context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, statusBarConfigurationController: StatusBarConfigurationController, + contentInsetsProvider: StatusBarContentInsetsProvider, ): StatusBarWindowController } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java index e4c6737856f0..6953bbf735f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java @@ -99,7 +99,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, @Assisted StatusBarConfigurationController statusBarConfigurationController, IWindowManager iWindowManager, - StatusBarContentInsetsProvider contentInsetsProvider, + @Assisted StatusBarContentInsetsProvider contentInsetsProvider, FragmentService fragmentService, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) { mContext = context; @@ -370,7 +370,8 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController StatusBarWindowControllerImpl create( @NonNull Context context, @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, - @NonNull StatusBarConfigurationController statusBarConfigurationController); + @NonNull StatusBarConfigurationController statusBarConfigurationController, + @NonNull StatusBarContentInsetsProvider contentInsetsProvider); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt index d83a2371ec92..051d463a8b97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -28,6 +28,7 @@ import com.android.systemui.display.data.repository.PerDisplayStoreImpl import com.android.systemui.display.data.repository.SingleDisplayStore import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore +import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -43,6 +44,7 @@ constructor( private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory, private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore, displayRepository: DisplayRepository, ) : StatusBarWindowControllerStore, @@ -64,6 +66,7 @@ constructor( statusBarDisplayContext.context, viewCaptureAwareWindowManager, statusBarConfigurationControllerStore.forDisplay(displayId), + statusBarContentInsetsProviderStore.forDisplay(displayId), ) } @@ -78,6 +81,7 @@ constructor( viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, factory: StatusBarWindowControllerImpl.Factory, statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore, ) : StatusBarWindowControllerStore, PerDisplayStore<StatusBarWindowController> by SingleDisplayStore( @@ -85,6 +89,7 @@ constructor( context, viewCaptureAwareWindowManager, statusBarConfigurationControllerStore.defaultDisplay, + statusBarContentInsetsProviderStore.defaultDisplay, ) ) { diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt index 80ea925eabc7..9dafba139bf7 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt @@ -21,7 +21,7 @@ import com.android.systemui.model.SysUiState import com.android.systemui.settings.DisplayTracker import com.android.systemui.shared.system.QuickStepContract import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class TouchpadGesturesInteractor( private val sysUiState: SysUiState, 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 e89a31f8fad3..bfdae626b5e8 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 @@ -27,7 +27,12 @@ import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenCon import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState +import com.android.systemui.util.kotlin.pairwiseBy +import kotlinx.coroutines.flow.Flow @Composable fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -41,25 +46,44 @@ fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni titleSuccessResId = R.string.touchpad_back_gesture_success_title, bodySuccessResId = R.string.touchpad_back_gesture_success_body, ), - animations = - TutorialScreenConfig.Animations( - educationResId = R.raw.trackpad_back_edu, - successResId = R.raw.trackpad_back_success, - ), + animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_back_edu), ) val recognizer = rememberBackGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.pairwiseBy(GestureState.NotStarted) { + previous, + current -> + val (startMarker, endMarker) = getMarkers(current) + current.toGestureUiState( + progressStartMarker = startMarker, + progressEndMarker = endMarker, + successAnimation = successAnimation(previous), + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable private fun rememberBackGestureRecognizer(resources: Resources): GestureRecognizer { val distance = - resources.getDimensionPixelSize( - com.android.internal.R.dimen.system_gestures_distance_threshold - ) + resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) return remember(distance) { BackGestureRecognizer(distance) } } +private fun getMarkers(it: GestureState): Pair<String, String> { + return if (it is GestureState.InProgress && it.direction == GestureDirection.LEFT) { + "gesture to L" to "end progress L" + } else "gesture to R" to "end progress R" +} + +private fun successAnimation(previous: GestureState): Int { + return if (previous is GestureState.InProgress && previous.direction == GestureDirection.LEFT) { + R.raw.trackpad_back_success_left + } else R.raw.trackpad_back_success_right +} + @Composable private fun rememberScreenColors(): TutorialScreenConfig.Colors { val onTertiary = MaterialTheme.colorScheme.onTertiary diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt index 7899f5b42a25..2332c0051c69 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt @@ -17,6 +17,7 @@ package com.android.systemui.touchpad.tutorial.ui.composable import androidx.activity.compose.BackHandler +import androidx.annotation.RawRes import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box @@ -31,23 +32,53 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig +import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished +import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureMonitor import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler +import kotlinx.coroutines.flow.Flow -fun GestureState.toTutorialActionState(): TutorialActionState { +sealed interface GestureUiState { + data object NotStarted : GestureUiState + + data class Finished(@RawRes val successAnimation: Int) : GestureUiState + + data class InProgress( + val progress: Float = 0f, + val progressStartMarker: String, + val progressEndMarker: String, + ) : GestureUiState +} + +fun GestureState.toGestureUiState( + progressStartMarker: String, + progressEndMarker: String, + successAnimation: Int, +): GestureUiState { + return when (this) { + GestureState.NotStarted -> NotStarted + is GestureState.InProgress -> + GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker) + is GestureState.Finished -> GestureUiState.Finished(successAnimation) + } +} + +fun GestureUiState.toTutorialActionState(): TutorialActionState { return when (this) { NotStarted -> TutorialActionState.NotStarted - // progress is disabled for now as views are not ready to handle varying progress - is InProgress -> TutorialActionState.InProgress(0f) - Finished -> TutorialActionState.Finished + is GestureUiState.InProgress -> + TutorialActionState.InProgress( + progress = progress, + startMarker = progressStartMarker, + endMarker = progressEndMarker, + ) + is Finished -> TutorialActionState.Finished(successAnimation) } } @@ -55,15 +86,13 @@ fun GestureState.toTutorialActionState(): TutorialActionState { fun GestureTutorialScreen( screenConfig: TutorialScreenConfig, gestureRecognizer: GestureRecognizer, + gestureUiStateFlow: Flow<GestureUiState>, onDoneButtonClicked: () -> Unit, onBack: () -> Unit, ) { BackHandler(onBack = onBack) - var gestureState: GestureState by remember { mutableStateOf(NotStarted) } var easterEggTriggered by remember { mutableStateOf(false) } - LaunchedEffect(gestureRecognizer) { - gestureRecognizer.addGestureStateCallback { gestureState = it } - } + val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted) val easterEggMonitor = EasterEggGestureMonitor { easterEggTriggered = true } val gestureHandler = remember(gestureRecognizer) { TouchpadGestureHandler(gestureRecognizer, easterEggMonitor) } @@ -84,7 +113,7 @@ fun GestureTutorialScreen( @Composable private fun TouchpadGesturesHandlingBox( gestureHandler: TouchpadGestureHandler, - gestureState: GestureState, + gestureState: GestureUiState, easterEggTriggered: Boolean, resetEasterEggFlag: () -> Unit, modifier: Modifier = Modifier, @@ -110,7 +139,7 @@ private fun TouchpadGesturesHandlingBox( .pointerInteropFilter( onTouchEvent = { event -> // FINISHED is the final state so we don't need to process touches anymore - if (gestureState == Finished) { + if (gestureState is Finished) { false } else { gestureHandler.onMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index 3ddf760b9704..bcf4bad9991d 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -40,22 +43,26 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni titleSuccessResId = R.string.touchpad_home_gesture_success_title, bodySuccessResId = R.string.touchpad_home_gesture_success_body, ), - animations = - TutorialScreenConfig.Animations( - educationResId = R.raw.trackpad_home_edu, - successResId = R.raw.trackpad_home_success, - ), + animations = TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_home_edu), ) val recognizer = rememberHomeGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMarker = "drag with gesture", + progressEndMarker = "release playback realtime", + successAnimation = R.raw.trackpad_home_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer { val distance = - resources.getDimensionPixelSize( - com.android.internal.R.dimen.system_gestures_distance_threshold - ) + resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) return remember(distance) { HomeGestureRecognizer(distance) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt index 30a21bf3b632..680b670f4d97 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt @@ -25,8 +25,11 @@ import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig import com.android.systemui.inputdevice.tutorial.ui.composable.rememberColorFilterProperty import com.android.systemui.res.R +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map @Composable fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) { @@ -41,21 +44,26 @@ fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body, ), animations = - TutorialScreenConfig.Animations( - educationResId = R.raw.trackpad_recent_apps_edu, - successResId = R.raw.trackpad_recent_apps_success, - ), + TutorialScreenConfig.Animations(educationResId = R.raw.trackpad_recent_apps_edu), ) val recognizer = rememberRecentAppsGestureRecognizer(LocalContext.current.resources) - GestureTutorialScreen(screenConfig, recognizer, onDoneButtonClicked, onBack) + val gestureUiState: Flow<GestureUiState> = + remember(recognizer) { + GestureFlowAdapter(recognizer).gestureStateAsFlow.map { + it.toGestureUiState( + progressStartMarker = "drag with gesture", + progressEndMarker = "onPause", + successAnimation = R.raw.trackpad_recent_apps_success, + ) + } + } + GestureTutorialScreen(screenConfig, recognizer, gestureUiState, onDoneButtonClicked, onBack) } @Composable private fun rememberRecentAppsGestureRecognizer(resources: Resources): GestureRecognizer { val distance = - resources.getDimensionPixelSize( - com.android.internal.R.dimen.system_gestures_distance_threshold - ) + resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) val velocity = resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold) return remember(distance, velocity) { RecentAppsGestureRecognizer(distance, velocity) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt index 80f800390852..35ea0ea3e048 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt @@ -18,6 +18,9 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.util.MathUtils import android.view.MotionEvent +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.LEFT +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection.RIGHT +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import kotlin.math.abs /** @@ -33,6 +36,10 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) @@ -40,7 +47,13 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback, gestureState, isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx }, - progress = { MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx)) }, + progress = ::getProgress, ) } + + private fun getProgress(it: Moving): InProgress { + val direction = if (it.deltaX > 0) RIGHT else LEFT + val value = MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx)) + return InProgress(value, direction) + } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt new file mode 100644 index 000000000000..23e31b0a9efd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt @@ -0,0 +1,30 @@ +/* + * 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.ui.gesture + +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +class GestureFlowAdapter(gestureRecognizer: GestureRecognizer) { + + val gestureStateAsFlow: Flow<GestureState> = conflatedCallbackFlow { + val callback: (GestureState) -> Unit = { trySend(it) } + gestureRecognizer.addGestureStateCallback(callback) + awaitClose { gestureRecognizer.clearGestureStateCallback() } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt index d146268304a6..68a2ef9528eb 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureRecognizer.kt @@ -22,6 +22,8 @@ import java.util.function.Consumer /** Based on passed [MotionEvent]s recognizes different states of gesture and notifies callback. */ interface GestureRecognizer : Consumer<MotionEvent> { fun addGestureStateCallback(callback: (GestureState) -> Unit) + + fun clearGestureStateCallback() } fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt index b513c49371a4..f27ddb515c24 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureState.kt @@ -21,5 +21,11 @@ sealed interface GestureState { data object Finished : GestureState - data class InProgress(val progress: Float = 0f) : GestureState + data class InProgress(val progress: Float = 0f, val direction: GestureDirection? = null) : + GestureState +} + +enum class GestureDirection { + LEFT, + RIGHT, } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt index f19467726def..24f5d1f00794 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt @@ -21,7 +21,7 @@ inline fun updateGestureState( gestureStateChangedCallback: (GestureState) -> Unit, gestureState: DistanceGestureState?, isFinished: (Finished) -> Boolean, - progress: (Moving) -> Float, + progress: (Moving) -> GestureState.InProgress, ) { when (gestureState) { is Finished -> { @@ -32,7 +32,7 @@ inline fun updateGestureState( } } is Moving -> { - gestureStateChangedCallback(GestureState.InProgress(progress(gestureState))) + gestureStateChangedCallback(progress(gestureState)) } is Started -> gestureStateChangedCallback(GestureState.InProgress()) else -> {} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt index 2b84a4c50613..e10b8253ebad 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt @@ -18,6 +18,7 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.util.MathUtils import android.view.MotionEvent +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress /** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { @@ -29,6 +30,10 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) @@ -36,7 +41,7 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu gestureStateChangedCallback, gestureState, isFinished = { -it.deltaY >= gestureDistanceThresholdPx }, - progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) }, + progress = { InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt index 69b7c5edd750..c47888609a61 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt @@ -38,6 +38,10 @@ class RecentAppsGestureRecognizer( gestureStateChangedCallback = callback } + override fun clearGestureStateCallback() { + gestureStateChangedCallback = {} + } + override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) @@ -50,7 +54,9 @@ class RecentAppsGestureRecognizer( -state.deltaY >= gestureDistanceThresholdPx && abs(velocityTracker.calculateVelocity().value) <= velocityThresholdPxPerMs }, - progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) }, + progress = { + GestureState.InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) + }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt index d4686e28ce5f..dccd5900963c 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withTimeout /** @@ -86,7 +86,7 @@ constructor( if (!isDeviceFoldable(context.resources, deviceStateManager)) { return } - applicationScope.launch(backgroundDispatcher) { + applicationScope.launch(context = backgroundDispatcher) { deviceStateRepository.state .pairwise() .filter { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt index c5e98a1f1c02..ecc2f18c7bcd 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt @@ -54,7 +54,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout @@ -95,7 +95,7 @@ constructor( ) controller.init() - applicationScope.launch(bgDispatcher) { + applicationScope.launch(context = bgDispatcher) { powerInteractor.screenPowerState.collect { if (it == ScreenPowerState.SCREEN_ON) { readyCallback = null @@ -103,7 +103,7 @@ constructor( } } - applicationScope.launch(bgDispatcher) { + applicationScope.launch(context = bgDispatcher) { deviceStateRepository.state .map { it == DeviceStateRepository.DeviceState.FOLDED } .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt index a92137766c49..7705205e83e2 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt @@ -51,7 +51,7 @@ import java.util.concurrent.Executor import java.util.function.Consumer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch interface FullscreenLightRevealAnimation { fun init() @@ -106,7 +106,7 @@ constructor( rotationChangeProvider.addCallback(rotationWatcher) buildSurface { builder -> - applicationScope.launch(executor.asCoroutineDispatcher()) { + applicationScope.launch(context = executor.asCoroutineDispatcher()) { val overlayContainer = builder.build() SurfaceControl.Transaction() diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt index a6224dcec13f..65970978b4ec 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt @@ -29,7 +29,7 @@ import com.android.systemui.util.Utils.isDeviceFoldable import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.plus /** diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 7bf9efa78142..493aa8c11b18 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt index 1238789389ee..a7983605eac9 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext interface UserSwitcherRepository { diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt index 0a1724c189c8..9b9eb0778dd2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt @@ -41,7 +41,7 @@ import com.android.systemui.user.domain.model.ShowDialogRequestModel import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/RefreshUsersScheduler.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/RefreshUsersScheduler.kt index 8f36821a955e..b0eb0fba7dd9 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/RefreshUsersScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/RefreshUsersScheduler.kt @@ -26,7 +26,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates logic for pausing, unpausing, and scheduling a delayed job. */ @SysUISingleton @@ -41,7 +41,7 @@ constructor( private var isPaused = false fun pause() { - applicationScope.launch(mainDispatcher) { + applicationScope.launch(context = mainDispatcher) { isPaused = true scheduledUnpauseJob?.cancel() scheduledUnpauseJob = @@ -53,14 +53,14 @@ constructor( } fun unpauseAndRefresh() { - applicationScope.launch(mainDispatcher) { + applicationScope.launch(context = mainDispatcher) { isPaused = false refreshIfNotPaused() } } fun refreshIfNotPaused() { - applicationScope.launch(mainDispatcher) { + applicationScope.launch(context = mainDispatcher) { if (isPaused) { return@launch } diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt index 516cb46ec6ee..3662c78efb16 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt @@ -81,7 +81,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt index 8e40f68e27e9..bd230cdac221 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/StatusBarUserChipViewBinder.kt @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherCo import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @OptIn(InternalCoroutinesApi::class) object StatusBarUserChipViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt index c57870291055..edf00e9c80bb 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt @@ -44,7 +44,7 @@ import com.android.systemui.user.ui.viewmodel.UserActionViewModel import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import com.android.systemui.util.children import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Binds a user switcher to its view-model. */ object UserSwitcherViewBinder { diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt index 2d41f32efbe7..102fcc0c59f2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt @@ -41,7 +41,7 @@ import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Coordinates dialogs for user switcher logic. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt index bb907cc0055e..7e7527ea4be3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Returns a new [Flow] that combines the two most recent emissions from [this] using [transform]. @@ -168,7 +168,7 @@ fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Fl coroutineScope { val noVal = Any() val sampledRef = AtomicReference(noVal) - val job = launch(Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } } + val job = launch(context = Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } } collect { val sampled = sampledRef.get() if (sampled != noVal) { diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index 7f90242fee2e..315912406b6d 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** A class allowing Java classes to collect on Kotlin flows. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Parallel.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Parallel.kt index a47a2d67070f..0b77e8f0c552 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Parallel.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Parallel.kt @@ -16,7 +16,7 @@ package com.android.systemui.util.kotlin import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt index 2e551f1e1bee..991c73e2d7fd 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt @@ -18,7 +18,7 @@ package com.android.systemui.util.kotlin import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Runs the given [blocks] in parallel, returning the result of the first one to complete, and diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index ea8709f7d65c..5d0b0d55e1f6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -26,7 +26,7 @@ import com.android.app.tracing.TraceUtils.trace import com.android.systemui.coroutines.newTracingContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt index c5deca214e28..4b03df6b0070 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt @@ -29,7 +29,7 @@ import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrTh import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt index e8367315c3c9..f56c0b98dde4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt @@ -23,7 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * This class is a bridge between diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 3d2ebf29cbb9..07509e6368fb 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -64,6 +64,8 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RotateDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.RoundRectShape; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Debug; @@ -115,6 +117,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.view.RotationPolicy; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; +import com.android.systemui.Flags; import com.android.systemui.Prefs; import com.android.systemui.dump.DumpManager; import com.android.systemui.haptics.slider.HapticSliderViewBinder; @@ -656,6 +659,11 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mRingerIcon = mRinger.findViewById(R.id.ringer_icon); } + if (Flags.hideRingerButtonInSingleVolumeMode() && AudioSystem.isSingleVolume(mContext)) { + mRingerAndDrawerContainer.setVisibility(INVISIBLE); + mRinger.setVisibility(INVISIBLE); + } + mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon); mSelectedRingerContainer = mDialog.findViewById( R.id.volume_new_ringer_active_icon_container); @@ -2341,10 +2349,31 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, return; } - final ColorDrawable solidDrawable = new ColorDrawable( + LayerDrawable background; + // mRingerAndDrawerContainer has rounded corner. + // But when it's not visible, mTopContainer needs to have rounded corner. + if (Flags.hideRingerButtonInSingleVolumeMode() + && mRingerAndDrawerContainer.getVisibility() != VISIBLE + ) { + float[] radius = new float[] { + mDialogCornerRadius, mDialogCornerRadius, // Top-left corner + mDialogCornerRadius, mDialogCornerRadius, // Top-right corner + 0, 0, // Bottom-right corner + 0, 0 // Bottom-left corner + }; + + ShapeDrawable roundedDrawable = new ShapeDrawable( + new RoundRectShape(radius, null, null)); + roundedDrawable.getPaint().setColor(Utils.getColorAttrDefaultColor( + mContext, com.android.internal.R.attr.colorSurface)); + + background = new LayerDrawable(new Drawable[] { roundedDrawable }); + } else { + final ColorDrawable solidDrawable = new ColorDrawable( Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface)); - final LayerDrawable background = new LayerDrawable(new Drawable[] { solidDrawable }); + background = new LayerDrawable(new Drawable[] { solidDrawable }); + } // Size the solid color to match the primary volume row. In landscape, extend it upwards // slightly so that it fills in the bottom corners of the ringer icon, whose background is diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt index 4b7a9782cc6b..1a198067c572 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt @@ -23,7 +23,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class VolumeDialogPlugin @Inject diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt index 3d125b81cbd2..fa1088426351 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt @@ -105,8 +105,8 @@ constructor( scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags)) } - override fun onAccessibilityModeChanged(showA11yStream: Boolean) { - scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream)) + override fun onAccessibilityModeChanged(showA11yStream: Boolean?) { + scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true)) } // Captions button is remove from the Volume Dialog diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt index 2668589be1b3..fb108c5b7b15 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt @@ -60,7 +60,7 @@ constructor( ) { @SuppressLint("SharedFlowCreation") - private val mutableDismissDialogEvents = MutableSharedFlow<Unit>() + private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1) val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility init { @@ -74,7 +74,7 @@ constructor( .mapNotNull { it.toVisibilityModel() } .onEach { model -> updateVisibility { model } - if (model is VolumeDialogVisibilityModel.Visible) { + if (model is Visible) { resetDismissTimeout() } } @@ -87,17 +87,17 @@ constructor( */ fun dismissDialog(reason: Int) { updateVisibility { visibilityModel -> - if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) { + if (visibilityModel is Dismissed) { visibilityModel } else { - VolumeDialogVisibilityModel.Dismissed(reason) + Dismissed(reason) } } } /** Resets current dialog timeout. */ - suspend fun resetDismissTimeout() { - mutableDismissDialogEvents.emit(Unit) + fun resetDismissTimeout() { + mutableDismissDialogEvents.tryEmit(Unit) } private fun updateVisibility( diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt deleted file mode 100644 index 73b97f642ec9..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepository.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.volume.dialog.ringer.data - -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.update - -/** Stores the state of volume dialog ringer model */ -@SysUISingleton -class VolumeDialogRingerRepository @Inject constructor() { - - private val mutableRingerModel = MutableStateFlow<VolumeDialogRingerModel?>(null) - val ringerModel: Flow<VolumeDialogRingerModel> = mutableRingerModel.filterNotNull() - - fun updateRingerModel(update: (current: VolumeDialogRingerModel?) -> VolumeDialogRingerModel) { - mutableRingerModel.update(update) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt new file mode 100644 index 000000000000..7265b82148ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt @@ -0,0 +1,84 @@ +/* + * 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.volume.dialog.ringer.domain + +import android.media.AudioManager +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.provider.Settings +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.stateIn + +/** Exposes [VolumeDialogRingerModel]. */ +@VolumeDialog +class VolumeDialogRingerInteractor +@Inject +constructor( + @VolumeDialog private val coroutineScope: CoroutineScope, + volumeDialogStateInteractor: VolumeDialogStateInteractor, + private val controller: VolumeDialogController, +) { + + val ringerModel: Flow<VolumeDialogRingerModel> = + volumeDialogStateInteractor.volumeDialogState + .mapNotNull { toRingerModel(it) } + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() + + private fun toRingerModel(state: VolumeDialogStateModel): VolumeDialogRingerModel? { + return state.streamModels[AudioManager.STREAM_RING]?.let { + VolumeDialogRingerModel( + availableModes = + mutableListOf(RingerMode(RINGER_MODE_NORMAL), RingerMode(RINGER_MODE_SILENT)) + .also { list -> + if (controller.hasVibrator()) { + list.add(RingerMode(RINGER_MODE_VIBRATE)) + } + }, + currentRingerMode = RingerMode(state.ringerModeInternal), + isEnabled = + !(state.zenMode == Settings.Global.ZEN_MODE_ALARMS || + state.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS || + (state.zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS && + state.disallowRinger)), + isMuted = it.level == 0 || it.muted, + level = it.level, + levelMax = it.levelMax, + ) + } + } + + fun setRingerMode(ringerMode: RingerMode) { + controller.setRingerMode(ringerMode.value, false) + } + + fun scheduleTouchFeedback() { + controller.scheduleTouchFeedback() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.kt new file mode 100644 index 000000000000..78d2d1686096 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerButtonViewModel.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.volume.dialog.ringer.ui.viewmodel + +import android.annotation.DrawableRes +import android.annotation.StringRes +import com.android.settingslib.volume.shared.model.RingerMode + +/** Models ringer button that corresponds to each ringer mode. */ +data class RingerButtonViewModel( + /** Image resource id for the image button. */ + @DrawableRes val imageResId: Int, + /** Content description for a11y. */ + @StringRes val contentDescriptionResId: Int, + /** Hint label for accessibility use. */ + @StringRes val hintLabelResId: Int, + /** Used to notify view model when button is clicked. */ + val ringerMode: RingerMode, +) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.kt new file mode 100644 index 000000000000..f3218370c4dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerDrawerState.kt @@ -0,0 +1,34 @@ +/* + * 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.volume.dialog.ringer.ui.viewmodel + +import com.android.settingslib.volume.shared.model.RingerMode + +/** Models volume dialog ringer drawer state */ +sealed interface RingerDrawerState { + + /** When clicked to open drawer */ + data class Open(val mode: RingerMode) : RingerDrawerState + + /** When clicked to close drawer */ + data class Closed(val mode: RingerMode) : RingerDrawerState + + /** Initial state when volume dialog is shown with a closed drawer. */ + interface Initial : RingerDrawerState { + companion object : Initial + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt new file mode 100644 index 000000000000..a09bfebfd3e3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt @@ -0,0 +1,27 @@ +/* + * 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.volume.dialog.ringer.ui.viewmodel + +/** Models volume dialog ringer */ +data class RingerViewModel( + /** List of the available buttons according to the available modes */ + val availableButtons: List<RingerButtonViewModel?>, + /** The index of the currently selected button */ + val currentButtonIndex: Int, + /** For open and close animations */ + val drawerState: RingerDrawerState, +) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt new file mode 100644 index 000000000000..ac82ae39ba5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt @@ -0,0 +1,172 @@ +/* + * 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.volume.dialog.ringer.ui.viewmodel + +import android.media.AudioAttributes +import android.media.AudioManager.RINGER_MODE_NORMAL +import android.media.AudioManager.RINGER_MODE_SILENT +import android.media.AudioManager.RINGER_MODE_VIBRATE +import android.os.VibrationEffect +import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.res.R +import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.volume.Events +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.ringer.domain.VolumeDialogRingerInteractor +import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel +import com.android.systemui.volume.dialog.shared.VolumeDialogLogger +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn + +private const val TAG = "VolumeDialogRingerDrawerViewModel" + +class VolumeDialogRingerDrawerViewModel +@AssistedInject +constructor( + @VolumeDialog private val coroutineScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val interactor: VolumeDialogRingerInteractor, + private val vibrator: VibratorHelper, + private val volumeDialogLogger: VolumeDialogLogger, +) { + + private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial) + + val ringerViewModel: Flow<RingerViewModel> = + combine(interactor.ringerModel, drawerState) { ringerModel, state -> + ringerModel.toViewModel(state) + } + .flowOn(backgroundDispatcher) + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() + + // Vibration attributes. + private val sonificiationVibrationAttributes = + AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build() + + fun onRingerButtonClicked(ringerMode: RingerMode) { + if (drawerState.value is RingerDrawerState.Open) { + Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value) + provideTouchFeedback(ringerMode) + interactor.setRingerMode(ringerMode) + } + drawerState.value = + when (drawerState.value) { + is RingerDrawerState.Initial -> { + RingerDrawerState.Open(ringerMode) + } + is RingerDrawerState.Open -> { + RingerDrawerState.Closed(ringerMode) + } + is RingerDrawerState.Closed -> { + RingerDrawerState.Open(ringerMode) + } + } + } + + private fun provideTouchFeedback(ringerMode: RingerMode) { + when (ringerMode.value) { + RINGER_MODE_NORMAL -> { + interactor.scheduleTouchFeedback() + null + } + RINGER_MODE_SILENT -> VibrationEffect.get(VibrationEffect.EFFECT_CLICK) + RINGER_MODE_VIBRATE -> VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK) + else -> VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK) + }?.let { vibrator.vibrate(it, sonificiationVibrationAttributes) } + } + + private fun VolumeDialogRingerModel.toViewModel( + drawerState: RingerDrawerState + ): RingerViewModel { + val currentIndex = availableModes.indexOf(currentRingerMode) + if (currentIndex == -1) { + volumeDialogLogger.onCurrentRingerModeIsUnsupported(currentRingerMode) + } + return RingerViewModel( + availableButtons = availableModes.map { mode -> toButtonViewModel(mode) }, + currentButtonIndex = currentIndex, + drawerState = drawerState, + ) + } + + private fun VolumeDialogRingerModel.toButtonViewModel( + ringerMode: RingerMode + ): RingerButtonViewModel? { + return when (ringerMode.value) { + RINGER_MODE_SILENT -> + RingerButtonViewModel( + imageResId = R.drawable.ic_speaker_mute, + contentDescriptionResId = R.string.volume_ringer_status_silent, + hintLabelResId = R.string.volume_ringer_hint_unmute, + ringerMode = ringerMode, + ) + RINGER_MODE_VIBRATE -> + RingerButtonViewModel( + imageResId = R.drawable.ic_volume_ringer_vibrate, + contentDescriptionResId = R.string.volume_ringer_status_vibrate, + hintLabelResId = R.string.volume_ringer_hint_vibrate, + ringerMode = ringerMode, + ) + RINGER_MODE_NORMAL -> + when { + isMuted && isEnabled -> + RingerButtonViewModel( + imageResId = R.drawable.ic_speaker_mute, + contentDescriptionResId = R.string.volume_ringer_status_normal, + hintLabelResId = R.string.volume_ringer_hint_unmute, + ringerMode = ringerMode, + ) + + availableModes.contains(RingerMode(RINGER_MODE_VIBRATE)) -> + RingerButtonViewModel( + imageResId = R.drawable.ic_speaker_on, + contentDescriptionResId = R.string.volume_ringer_status_normal, + hintLabelResId = R.string.volume_ringer_hint_vibrate, + ringerMode = ringerMode, + ) + + else -> + RingerButtonViewModel( + imageResId = R.drawable.ic_speaker_on, + contentDescriptionResId = R.string.volume_ringer_status_normal, + hintLabelResId = R.string.volume_ringer_hint_mute, + ringerMode = ringerMode, + ) + } + else -> null + } + } + + @AssistedFactory + interface Factory { + fun create(): VolumeDialogRingerDrawerViewModel + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt index 59c38c019823..9a3aa7e3d79f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.volume.dialog.shared +import com.android.settingslib.volume.shared.model.RingerMode import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.log.dagger.VolumeLog @@ -43,4 +44,13 @@ class VolumeDialogLogger @Inject constructor(@VolumeLog private val logBuffer: L { "Dismiss: ${Events.DISMISS_REASONS[int1]}" }, ) } + + fun onCurrentRingerModeIsUnsupported(ringerMode: RingerMode) { + logBuffer.log( + TAG, + LogLevel.DEBUG, + { int1 = ringerMode.value }, + { "Current ringer mode: $int1, ringer mode is unsupported in ringer drawer options" }, + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt index f78a8dcabc1c..876bf2c4a154 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt @@ -25,6 +25,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.mapNotNull /** Operates a state of particular slider of the Volume Dialog. */ @@ -37,12 +38,23 @@ constructor( ) { val slider: Flow<VolumeDialogStreamModel> = - volumeDialogStateInteractor.volumeDialogState.mapNotNull { - it.streamModels[sliderType.audioStream] - } + volumeDialogStateInteractor.volumeDialogState + .mapNotNull { + it.streamModels[sliderType.audioStream]?.run { + if (level < levelMin || level > levelMax) { + copy(level = level.coerceIn(levelMin, levelMax)) + } else { + this + } + } + } + .distinctUntilChanged() fun setStreamVolume(userLevel: Int) { - volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel) + with(volumeDialogController) { + setStreamVolume(sliderType.audioStream, userLevel) + setActiveStream(sliderType.audioStream) + } } @VolumeDialogScope diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt index 25a5f287c21f..5c4d53aaf2c5 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt @@ -16,32 +16,55 @@ package com.android.systemui.volume.dialog.sliders.ui +import android.animation.Animator +import android.animation.ObjectAnimator import android.view.View -import androidx.lifecycle.viewmodel.compose.viewModel +import android.view.animation.DecelerateInterpolator import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.lifecycle.viewModel +import com.android.systemui.res.R import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel +import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory +import com.android.systemui.volume.dialog.ui.utils.awaitAnimation +import com.google.android.material.slider.LabelFormatter +import com.google.android.material.slider.Slider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlin.math.roundToInt import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L class VolumeDialogSliderViewBinder @AssistedInject -constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) { +constructor( + @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel, + private val jankListenerFactory: JankListenerFactory, +) { fun bind(view: View) { with(view) { + val sliderView: Slider = + requireViewById<Slider>(R.id.volume_dialog_slider).apply { + labelBehavior = LabelFormatter.LABEL_GONE + } repeatWhenAttached { viewModel( traceName = "VolumeDialogSliderViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelProvider() }, ) { viewModel -> - setSnapshotBinding {} + sliderView.addOnChangeListener { _, value, fromUser -> + viewModel.setStreamVolume(value.roundToInt(), fromUser) + } + + viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this) awaitCancellation() } @@ -49,6 +72,19 @@ constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderVie } } + private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) { + with(slider) { + valueFrom = levelMin.toFloat() + valueTo = levelMax.toFloat() + // coerce the current value to the new value range before animating it + value = value.coerceIn(valueFrom, valueTo) + setValueAnimated( + level.toFloat(), + jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS), + ) + } + } + @AssistedFactory @VolumeDialogScope interface Factory { @@ -58,3 +94,16 @@ constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderVie ): VolumeDialogSliderViewBinder } } + +private suspend fun Slider.setValueAnimated( + newValue: Float, + jankListener: Animator.AnimatorListener, +) { + ObjectAnimator.ofFloat(value, newValue) + .apply { + duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS + interpolator = DecelerateInterpolator() + addListener(jankListener) + } + .awaitAnimation<Float> { value = it } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt index 0a00f70b54f1..f486fe11bd68 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt @@ -16,14 +16,20 @@ package com.android.systemui.volume.dialog.sliders.ui +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.compose.ui.util.fastForEachIndexed import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.lifecycle.viewModel +import com.android.systemui.res.R import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel import javax.inject.Inject +import kotlin.math.abs import kotlinx.coroutines.awaitCancellation @VolumeDialogScope @@ -33,17 +39,44 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) fun bind(view: View) { with(view) { + val volumeDialog: View = requireViewById(R.id.volume_dialog) + val floatingSlidersContainer: ViewGroup = + requireViewById(R.id.volume_dialog_floating_sliders_container) repeatWhenAttached { viewModel( traceName = "VolumeDialogSlidersViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, factory = { viewModelFactory.create() }, ) { viewModel -> - setSnapshotBinding {} + setSnapshotBinding { + viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog) + val floatingSliderViewBinders = + viewModel.uiModel?.floatingSliderViewBinders ?: emptyList() + floatingSlidersContainer.ensureChildCount( + viewLayoutId = R.layout.volume_dialog_slider_floating, + count = floatingSliderViewBinders.size, + ) + floatingSliderViewBinders.fastForEachIndexed { index, viewBinder -> + viewBinder.bind(floatingSlidersContainer.getChildAt(index)) + } + } awaitCancellation() } } } } } + +private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) { + val childCountDelta = childCount - count + when { + childCountDelta > 0 -> { + removeViews(0, childCountDelta) + } + childCountDelta < 0 -> { + val inflater = LayoutInflater.from(context) + repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt index 7ee722d97bbc..ea0b49d5294e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt @@ -16,26 +16,85 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel +import com.android.systemui.util.time.SystemClock +import com.android.systemui.volume.Events +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +/* + This prevents volume slider updates while user interacts with it. This is needed due to the + flawed VolumeDialogControllerImpl. It has a single threaded message queue that handles all state + updates and doesn't skip sequential updates of the same stream. This leads to a bottleneck when + user rigorously adjusts the slider. + + Remove this when getting rid of the VolumeDialogControllerImpl as this doesn't happen in the + Volume Panel that uses the new coroutine-backed AudioRepository. +*/ +// TODO(b/375355785) remove this +private const val VOLUME_UPDATE_GRACE_PERIOD = 1000 + +@OptIn(ExperimentalCoroutinesApi::class) class VolumeDialogSliderViewModel @AssistedInject -constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) { +constructor( + @Assisted private val interactor: VolumeDialogSliderInteractor, + private val visibilityInteractor: VolumeDialogVisibilityInteractor, + @VolumeDialog private val coroutineScope: CoroutineScope, + private val systemClock: SystemClock, +) { + + private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null) - val model: Flow<VolumeDialogStreamModel> = interactor.slider + val model: Flow<VolumeDialogStreamModel> = + interactor.slider + .filter { + val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0 + getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD + } + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() - fun setStreamVolume(volume: Int) { - interactor.setStreamVolume(volume) + init { + userVolumeUpdates + .filterNotNull() + .mapLatest { volume -> + interactor.setStreamVolume(volume.newVolumeLevel) + Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, model.first().stream, volume) + } + .launchIn(coroutineScope) } + fun setStreamVolume(volume: Int, fromUser: Boolean) { + if (fromUser) { + visibilityInteractor.resetDismissTimeout() + userVolumeUpdates.value = + VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis()) + } + } + + private fun getTimestampMillis(): Long = systemClock.uptimeMillis() + @AssistedFactory interface Factory { fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel } + + private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt index b5b292fa4a66..22cf89fa6bde 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt @@ -16,6 +16,9 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel +import androidx.compose.runtime.getValue +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.lifecycle.Hydrator import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor @@ -24,10 +27,9 @@ import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinde import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -39,9 +41,10 @@ constructor( private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory, private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory, private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory, -) { +) : ExclusiveActivatable() { - val sliders: Flow<VolumeDialogSliderUiModel> = + private val hydrator = Hydrator("VolumeDialogSlidersViewModel") + private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> = slidersInteractor.sliders .distinctUntilChanged() .map { slidersModel -> @@ -52,7 +55,13 @@ constructor( ) } .stateIn(coroutineScope, SharingStarted.Eagerly, null) - .filterNotNull() + + val uiModel: VolumeDialogSliderUiModel? by + hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow) + + override suspend fun onActivated(): Nothing { + hydrator.activate() + } private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder = sliderViewBinderFactory.create { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt index 77733fe33275..eb9483f8ea68 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt @@ -20,6 +20,7 @@ import android.app.Dialog import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.ColorDrawable +import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager @@ -27,6 +28,7 @@ import com.android.systemui.res.R import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder +import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -40,6 +42,7 @@ class VolumeDialogBinder constructor( @VolumeDialog private val coroutineScope: CoroutineScope, private val volumeDialogViewBinder: VolumeDialogViewBinder, + private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, private val gravityViewModel: VolumeDialogGravityViewModel, ) { @@ -50,11 +53,11 @@ constructor( dialog.setContentView(R.layout.volume_dialog) dialog.setCanceledOnTouchOutside(true) - settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings)) - volumeDialogViewBinder.bind( - dialog, - dialog.requireViewById(R.id.volume_dialog_container), - ) + with(dialog.requireViewById<View>(R.id.volume_dialog_container)) { + slidersViewBinder.bind(this) + settingsButtonViewBinder.bind(this) + volumeDialogViewBinder.bind(dialog, this) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt index 4eae3b9a8da5..c7f5801a87c2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt @@ -17,8 +17,11 @@ package com.android.systemui.volume.dialog.ui.utils import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator import android.view.ViewPropertyAnimator import kotlin.coroutines.resume +import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.suspendCancellableCoroutine /** @@ -39,11 +42,12 @@ suspend fun ViewPropertyAnimator.suspendAnimate( } override fun onAnimationEnd(animation: Animator) { - continuation.resume(Unit) + continuation.resumeIfCan(Unit) animationListener?.onAnimationEnd(animation) } override fun onAnimationCancel(animation: Animator) { + continuation.resumeIfCan(Unit) animationListener?.onAnimationCancel(animation) } @@ -54,3 +58,30 @@ suspend fun ViewPropertyAnimator.suspendAnimate( ) continuation.invokeOnCancellation { this.cancel() } } + +/** + * Starts animation and suspends until it's finished. Cancels the animation if the running coroutine + * is cancelled. + */ +@Suppress("UNCHECKED_CAST") +suspend fun <T> ValueAnimator.awaitAnimation(onValueChanged: (T) -> Unit) { + suspendCancellableCoroutine { continuation -> + addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) = continuation.resumeIfCan(Unit) + + override fun onAnimationCancel(animation: Animator) = continuation.resumeIfCan(Unit) + } + ) + addUpdateListener { onValueChanged(it.animatedValue as T) } + + start() + continuation.invokeOnCancellation { cancel() } + } +} + +private fun <T> CancellableContinuation<T>.resumeIfCan(value: T) { + if (!isCancelled && !isCompleted) { + resume(value) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt index 9aed8ab8f2e2..293be94638db 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext interface AudioSharingInteractor { diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt index 12447577e945..5c5011057db6 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/startable/AudioModeLoggerStartable.kt @@ -24,7 +24,7 @@ import com.android.systemui.volume.panel.ui.VolumePanelUiEvent import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Logger for audio mode */ @VolumePanelScope diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt index 9e708433dfbf..b946ea8f79e9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Volume Panel captioning UI model. */ @VolumePanelScope diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt index a714f8078db7..dacd6c78b034 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt @@ -30,7 +30,7 @@ import javax.inject.Inject import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch interface MediaControllerInteractor { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt index 324579d1ee04..7ebc76dc2b95 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @VolumePanelScope class SpatialAudioViewModel diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index 2aa1ac99a400..1f44451f0cb4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** Models a particular slider state. */ class AudioStreamSliderViewModel diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt index 10714d1f41af..bb849cb1333e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch class CastVolumeSliderViewModel @AssistedInject diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt index 45732deb9aa6..96afbc1feaaf 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Controls the behaviour of the whole audio diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt index 09b1f45f179b..e3e08e8de6c8 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt @@ -14,7 +14,7 @@ import com.android.systemui.flags.Flags import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch /** * Serves as an intermediary between QuickAccessWalletService and ContextualCardManager (in PCC). @@ -44,7 +44,7 @@ constructor( override fun onBind(intent: Intent): IBinder { super.onBind(intent) if (registerNewWalletCardInBackground()) { - scope.launch(backgroundDispatcher) { + scope.launch(context = backgroundDispatcher) { controller.allWalletCards.collect { cards -> val cardsSize = cards.size Log.i(TAG, "Number of cards registered $cardsSize") diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt index eb4ff1722dcc..af84e4eaf0c5 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt index 54953c9c2574..9055d18a9f55 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt @@ -35,6 +35,4 @@ class NoopWallpaperRepository @Inject constructor() : WallpaperRepository { override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow() override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow() override var rootView: View? = null - - override fun setNotificationStackAbsoluteBottom(bottom: Float) {} } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt index efdd98d5498a..015b480eddc8 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt @@ -45,13 +45,12 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** A repository storing information about the current wallpaper. */ @@ -64,12 +63,6 @@ interface WallpaperRepository { /** Set rootView to get its windowToken afterwards */ var rootView: View? - - /** - * Set bottom of notifications from notification stack, and Magic Portrait will layout base on - * this value - */ - fun setNotificationStackAbsoluteBottom(bottom: Float) } @SysUISingleton @@ -106,7 +99,8 @@ constructor( .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE } /** The bottom of notification stack respect to the top of screen. */ - private val notificationStackAbsoluteBottom: MutableStateFlow<Float> = MutableStateFlow(0F) + private val notificationStackAbsoluteBottom: StateFlow<Float> = + keyguardRepository.notificationStackAbsoluteBottom /** The top of shortcut respect to the top of screen. */ private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop @@ -206,10 +200,6 @@ constructor( initialValue = false, ) - override fun setNotificationStackAbsoluteBottom(bottom: Float) { - notificationStackAbsoluteBottom.value = bottom - } - private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? { return withContext(bgDispatcher) { wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id) diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt index fe6977c367b5..88795cada716 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt @@ -21,10 +21,6 @@ import javax.inject.Inject import kotlinx.coroutines.flow.StateFlow class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) { - fun setNotificationStackAbsoluteBottom(bottom: Float) { - wallpaperRepository.setNotificationStackAbsoluteBottom(bottom) - } - val wallpaperSupportsAmbientMode: StateFlow<Boolean> = wallpaperRepository.wallpaperSupportsAmbientMode } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index c7b707d02cb3..2b167e4c5da4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.plugins.clocks.ClockFaceController import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockTickRate +import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData.ZenMode import com.android.systemui.res.R @@ -81,7 +82,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock @@ -151,6 +151,8 @@ class ClockEventControllerTest : SysuiTestCase() { .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) whenever(largeClockController.config) .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) + whenever(smallClockController.theme).thenReturn(ThemeConfig(true, null)) + whenever(largeClockController.theme).thenReturn(ThemeConfig(true, null)) zenModeRepository.addMode(MANUAL_DND_INACTIVE) @@ -205,14 +207,15 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) { - verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) - verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) + verify(smallClockEvents).onThemeChanged(any()) + verify(largeClockEvents).onThemeChanged(any()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) captor.value.onThemeChanged() - verify(events).onColorPaletteChanged(any()) + verify(smallClockEvents, times(2)).onThemeChanged(any()) + verify(largeClockEvents, times(2)).onThemeChanged(any()) } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 34ebba844926..b941fdef76b4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -159,6 +159,7 @@ import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.utils.FieldSetter; import org.junit.After; import org.junit.Assert; @@ -172,7 +173,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; -import org.mockito.internal.util.reflection.FieldSetter; import org.mockito.quality.Strictness; import java.util.ArrayList; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt index 1d1329ac550c..4e0ebae6a902 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.qs.tiles.OneHandedModeTile import com.android.systemui.qs.tiles.ReduceBrightColorsTile import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.utils.FieldSetter import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher @@ -46,7 +47,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito -import org.mockito.internal.util.reflection.FieldSetter import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 7889b3cd6cc3..61eeab3a8c07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -65,7 +65,6 @@ import com.android.systemui.haptics.msdl.msdlPlayer import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper -import com.android.systemui.statusbar.events.ANIMATING_OUT import com.android.systemui.testKosmos import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.FakeExecutor @@ -176,7 +175,7 @@ open class AuthContainerViewTest : SysuiTestCase() { BiometricStatusInteractorImpl( activityTaskManager, biometricStatusRepository, - fingerprintRepository + fingerprintRepository, ) iconProvider = IconProvider(context) // Set up default logo icon @@ -245,7 +244,7 @@ open class AuthContainerViewTest : SysuiTestCase() { @Test fun testIgnoresAnimatedInWhenDialogAnimatingOut() { val container = initializeFingerprintContainer(addToView = false) - container.mContainerState = ANIMATING_OUT + container.mContainerState = 4 // STATE_ANIMATING_OUT container.addToView() waitForIdleSync() @@ -278,7 +277,7 @@ open class AuthContainerViewTest : SysuiTestCase() { .onDismissed( eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), eq<ByteArray?>(null), /* credentialAttestation */ - eq(authContainer?.requestId ?: 0L) + eq(authContainer?.requestId ?: 0L), ) assertThat(container.parent).isNull() } @@ -292,13 +291,13 @@ open class AuthContainerViewTest : SysuiTestCase() { verify(callback) .onSystemEvent( eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL), - eq(authContainer?.requestId ?: 0L) + eq(authContainer?.requestId ?: 0L), ) verify(callback) .onDismissed( eq(AuthDialogCallback.DISMISSED_USER_CANCELED), eq<ByteArray?>(null), /* credentialAttestation */ - eq(authContainer?.requestId ?: 0L) + eq(authContainer?.requestId ?: 0L), ) assertThat(container.parent).isNull() } @@ -313,7 +312,7 @@ open class AuthContainerViewTest : SysuiTestCase() { .onDismissed( eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), eq<ByteArray?>(null), /* credentialAttestation */ - eq(authContainer?.requestId ?: 0L) + eq(authContainer?.requestId ?: 0L), ) assertThat(container.parent).isNull() } @@ -340,7 +339,7 @@ open class AuthContainerViewTest : SysuiTestCase() { .onDismissed( eq(AuthDialogCallback.DISMISSED_ERROR), eq<ByteArray?>(null), /* credentialAttestation */ - eq(authContainer?.requestId ?: 0L) + eq(authContainer?.requestId ?: 0L), ) assertThat(authContainer!!.parent).isNull() } @@ -454,7 +453,7 @@ open class AuthContainerViewTest : SysuiTestCase() { val container = initializeFingerprintContainer( authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, - verticalListContentView = PromptVerticalListContentView.Builder().build() + verticalListContentView = PromptVerticalListContentView.Builder().build(), ) // Two-step credential view should show - // 1. biometric prompt without sensor 2. credential view ui @@ -479,7 +478,7 @@ open class AuthContainerViewTest : SysuiTestCase() { val container = initializeFingerprintContainer( authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL, - contentViewWithMoreOptionsButton = contentView + contentViewWithMoreOptionsButton = contentView, ) waitForIdleSync() @@ -565,7 +564,7 @@ open class AuthContainerViewTest : SysuiTestCase() { } private fun initializeCredentialPasswordContainer( - addToView: Boolean = true, + addToView: Boolean = true ): TestAuthContainerView { whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20) whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))) @@ -597,25 +596,25 @@ open class AuthContainerViewTest : SysuiTestCase() { fingerprintProps = fingerprintSensorPropertiesInternal(), verticalListContentView = verticalListContentView, ), - addToView + addToView, ) private fun initializeCoexContainer( authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK, - addToView: Boolean = true + addToView: Boolean = true, ) = initializeContainer( TestAuthContainerView( authenticators = authenticators, fingerprintProps = fingerprintSensorPropertiesInternal(), - faceProps = faceSensorPropertiesInternal() + faceProps = faceSensorPropertiesInternal(), ), - addToView + addToView, ) private fun initializeContainer( view: TestAuthContainerView, - addToView: Boolean + addToView: Boolean, ): TestAuthContainerView { authContainer = view @@ -668,7 +667,7 @@ open class AuthContainerViewTest : SysuiTestCase() { biometricStatusInteractor, udfpsUtils, iconProvider, - activityTaskManager + activityTaskManager, ), { credentialViewModel }, fakeExecutor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt index ce37eee24e2a..4d138b488645 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt @@ -63,6 +63,7 @@ class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() { private lateinit var mockitoSession: StaticMockitoSession private lateinit var connectedAudioSharingMediaDeviceItem: DeviceItem private lateinit var connectedMediaDeviceItem: DeviceItem + private lateinit var inAudioSharingMediaDeviceItem: DeviceItem @Mock private lateinit var dialog: SystemUIDialog @Mock private lateinit var leAudioProfile: LeAudioProfile @Mock private lateinit var bluetoothDevice: BluetoothDevice @@ -80,6 +81,15 @@ class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() { iconWithDescription = null, background = null, ) + inAudioSharingMediaDeviceItem = + DeviceItem( + type = DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = kosmos.cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null, + ) connectedAudioSharingMediaDeviceItem = DeviceItem( type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE, @@ -131,6 +141,19 @@ class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() { } @Test + fun testOnClick_inAudioSharingMediaDevice_doNothing() { + with(kosmos) { + testScope.runTest { + bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true) + actionInteractorImpl.onClick(inAudioSharingMediaDeviceItem, dialog) + + verify(dialogTransitionAnimator, never()) + .showFromDialog(any(), any(), eq(null), anyBoolean()) + } + } + } + + @Test fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() { with(kosmos) { testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt index 10c3457066cb..2ff8cbcf34ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt @@ -133,36 +133,26 @@ class DeviceItemFactoryTest : SysuiTestCase() { @Test fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() { - // Flags.FLAG_ENABLE_LE_AUDIO_SHARING off or the device doesn't support broadcast - // source or assistant. - `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) - assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager) + .isFilterMatched(context, cachedDevice, audioManager, false) ) .isFalse() } @Test fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_false() { - // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and - // assistant. - `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(true) assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager) + .isFilterMatched(context, cachedDevice, audioManager, true) ) .isFalse() } @Test fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_false() { - // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and - // assistant. - `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false) `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true) `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any())) @@ -170,16 +160,13 @@ class DeviceItemFactoryTest : SysuiTestCase() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager) + .isFilterMatched(context, cachedDevice, audioManager, true) ) .isFalse() } @Test fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTrue() { - // Flags.FLAG_ENABLE_LE_AUDIO_SHARING on and the device support broadcast source and - // assistant. - `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) `when`(BluetoothUtils.isActiveMediaDevice(any())).thenReturn(false) `when`(BluetoothUtils.isAvailableMediaBluetoothDevice(any(), any())).thenReturn(true) `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any())) @@ -187,7 +174,7 @@ class DeviceItemFactoryTest : SysuiTestCase() { assertThat( AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager) - .isFilterMatched(context, cachedDevice, audioManager) + .isFilterMatched(context, cachedDevice, audioManager, true) ) .isTrue() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt index c39b9a606cfe..42dc50d77d05 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.testKosmos import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher @@ -48,6 +49,7 @@ import org.mockito.junit.MockitoRule class DeviceItemInteractorTest : SysuiTestCase() { @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val kosmos = testKosmos() @Mock private lateinit var bluetoothTileDialogRepository: BluetoothTileDialogRepository @@ -99,6 +101,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -107,7 +110,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ true }, deviceItem1)), emptyList(), testScope.backgroundScope, - dispatcher + dispatcher, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -126,6 +129,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -134,7 +138,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ false }, deviceItem1)), emptyList(), testScope.backgroundScope, - dispatcher + dispatcher, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -153,6 +157,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -161,7 +166,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ true }, deviceItem1)), emptyList(), testScope.backgroundScope, - dispatcher + dispatcher, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -180,6 +185,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -187,11 +193,11 @@ class DeviceItemInteractorTest : SysuiTestCase() { logger, listOf( createFactory({ false }, deviceItem1), - createFactory({ true }, deviceItem2) + createFactory({ true }, deviceItem2), ), emptyList(), testScope.backgroundScope, - dispatcher + dispatcher, ) val latest by collectLastValue(interactor.deviceItemUpdate) @@ -210,6 +216,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -218,19 +225,19 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf( createFactory( { cachedDevice -> cachedDevice.device == device1 }, - deviceItem1 + deviceItem1, ), createFactory( { cachedDevice -> cachedDevice.device == device2 }, - deviceItem2 - ) + deviceItem2, + ), ), listOf( DeviceItemType.SAVED_BLUETOOTH_DEVICE, - DeviceItemType.CONNECTED_BLUETOOTH_DEVICE + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, ), testScope.backgroundScope, - dispatcher + dispatcher, ) `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) @@ -251,6 +258,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -259,16 +267,16 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf( createFactory( { cachedDevice -> cachedDevice.device == device1 }, - deviceItem1 + deviceItem1, ), createFactory( { cachedDevice -> cachedDevice.device == device2 }, - deviceItem2 - ) + deviceItem2, + ), ), listOf(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE), testScope.backgroundScope, - dispatcher + dispatcher, ) `when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) @@ -291,6 +299,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { interactor = DeviceItemInteractor( bluetoothTileDialogRepository, + kosmos.audioSharingInteractor, audioManager, adapter, localBluetoothManager, @@ -299,7 +308,7 @@ class DeviceItemInteractorTest : SysuiTestCase() { listOf(createFactory({ true }, deviceItem2)), emptyList(), testScope.backgroundScope, - dispatcher + dispatcher, ) val latest by collectLastValue(interactor.deviceItemUpdate) val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate) @@ -312,13 +321,14 @@ class DeviceItemInteractorTest : SysuiTestCase() { private fun createFactory( isFilterMatchFunc: (CachedBluetoothDevice) -> Boolean, - deviceItem: DeviceItem + deviceItem: DeviceItem, ): DeviceItemFactory { return object : DeviceItemFactory() { override fun isFilterMatched( context: Context, cachedDevice: CachedBluetoothDevice, - audioManager: AudioManager + audioManager: AudioManager, + audioSharingAvailable: Boolean, ) = isFilterMatchFunc(cachedDevice) override fun create(context: Context, cachedDevice: CachedBluetoothDevice) = deviceItem diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 3d1a0d0cef3c..96f4a60271d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -481,6 +481,25 @@ public class DozeTriggersTest extends SysuiTestCase { verify(mAuthController).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); } + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void udfpsLongPress_triggeredWhenDoze() { + // GIVEN device is DOZE + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + // WHEN udfps long-press is triggered + mTriggers.onSensor(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, 100, 100, + new float[]{0, 1, 2, 3, 4}); + + // THEN the pulse is NOT dropped + verify(mDozeLog, never()).tracePulseDropped(anyString(), any()); + + // WHEN the screen state is OFF + mTriggers.onScreenState(Display.STATE_OFF); + + // THEN aod interrupt never be sent + verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat()); + } @Test public void udfpsLongPress_dozeState_notRegistered() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index fc2ad60cfa72..eae828562223 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -53,7 +53,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CON import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT import com.android.systemui.shade.carrier.ShadeCarrierGroup import com.android.systemui.shade.carrier.ShadeCarrierGroupController -import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.StatusOverlayHoverListenerFactory import com.android.systemui.statusbar.phone.ui.StatusBarIconController @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.statusbar.policy.NextAlarmController import com.android.systemui.statusbar.policy.VariableDateView import com.android.systemui.statusbar.policy.VariableDateViewController +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -93,6 +94,10 @@ private val EMPTY_CHANGES = ConstraintsChanges() @RunWith(AndroidTestingRunner::class) class ShadeHeaderControllerTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val insetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore + private val insetsProvider = insetsProviderStore.defaultDisplay + @Mock(answer = Answers.RETURNS_MOCKS) private lateinit var view: MotionLayout @Mock private lateinit var statusIcons: StatusIconContainer @Mock private lateinit var statusBarIconController: StatusBarIconController @@ -107,7 +112,6 @@ class ShadeHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var batteryMeterView: BatteryMeterView @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController @Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController - @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider @Mock private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory @Mock private lateinit var variableDateViewController: VariableDateViewController @Mock private lateinit var dumpManager: DumpManager @@ -190,7 +194,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { statusBarIconController, iconManagerFactory, privacyIconsController, - insetsProvider, + insetsProviderStore, configurationController, variableDateViewControllerFactory, batteryMeterViewController, @@ -201,7 +205,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { qsBatteryModeController, nextAlarmController, activityStarter, - mStatusOverlayHoverListenerFactory + mStatusOverlayHoverListenerFactory, ) whenever(view.isAttachedToWindow).thenReturn(true) shadeHeaderController.init() @@ -597,7 +601,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { anyInt(), anyInt(), anyInt(), - anyInt() + anyInt(), ) ) .thenReturn(mockConstraintsChanges) @@ -631,7 +635,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { anyInt(), anyInt(), anyInt(), - anyInt() + anyInt(), ) ) .thenReturn(mockConstraintsChanges) @@ -751,7 +755,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever( combinedShadeHeadersConstraintManager.centerCutoutConstraints( Mockito.anyBoolean(), - anyInt() + anyInt(), ) ) .thenReturn(mockConstraintsChanges) @@ -788,7 +792,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever( combinedShadeHeadersConstraintManager.centerCutoutConstraints( Mockito.anyBoolean(), - anyInt() + anyInt(), ) ) .thenReturn(mockConstraintsChanges) @@ -899,7 +903,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { top: Int, right: Int, bottom: Int, - listener: View.OnLayoutChangeListener + listener: View.OnLayoutChangeListener, ) { val oldLeft = this.left val oldTop = this.top @@ -920,7 +924,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { left, top, right, - bottom + bottom, ) } @@ -941,7 +945,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { /* left= */ insets.first, /* top= */ 0, /* right= */ insets.second, - /* bottom= */ 0 + /* bottom= */ 0, ) ) whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(cornerCutout) @@ -968,7 +972,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { anyInt(), anyInt(), anyInt(), - anyInt() + anyInt(), ) ) .thenReturn(EMPTY_CHANGES) @@ -977,7 +981,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever( combinedShadeHeadersConstraintManager.centerCutoutConstraints( Mockito.anyBoolean(), - anyInt() + anyInt(), ) ) .thenReturn(EMPTY_CHANGES) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index a75d7b23a92c..da0029ff6746 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -21,6 +21,8 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; @@ -777,6 +779,24 @@ public class KeyguardIndicationControllerTest extends KeyguardIndicationControll } @Test + public void indicationAreaHidden_untilBatteryInfoArrives() { + createController(); + // level of -1 indicates missing info + BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_UNKNOWN, + -1 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */, + 0 /* maxChargingWattage */, true /* present */); + + mController.setVisible(true); + mStatusBarStateListener.onDozingChanged(true); + reset(mIndicationArea); + + mController.getKeyguardCallback().onRefreshBatteryInfo(status); + // VISIBLE is always called first + verify(mIndicationArea).setVisibility(VISIBLE); + verify(mIndicationArea).setVisibility(GONE); + } + + @Test public void onRefreshBatteryInfo_computesChargingTime() throws RemoteException { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt index 4e7de8101342..4cad5f7378bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt @@ -29,21 +29,24 @@ import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.dump.DumpManager import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.statusbar.BatteryStatusChip +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimationQueued +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.ShowingPersistentDot import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock -import junit.framework.Assert.assertEquals import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test @@ -54,6 +57,10 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) @@ -120,12 +127,12 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { val batteryChip = createAndScheduleFakeBatteryEvent() // assert that animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // skip debounce delay advanceTimeBy(DEBOUNCE_DELAY + 1) // status chip starts animating in after debounce delay - assertEquals(ANIMATING_IN, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingIn, systemStatusAnimationScheduler.animationState.value) assertEquals(0f, batteryChip.contentView.alpha) assertEquals(0f, batteryChip.view.alpha) verify(listener, times(1)).onSystemEventAnimationBegin() @@ -134,14 +141,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) advanceTimeBy(APPEAR_ANIMATION_DURATION) // assert that status chip is visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, batteryChip.contentView.alpha) assertEquals(1f, batteryChip.view.alpha) // skip status chip display time advanceTimeBy(DISPLAY_LENGTH + 1) - // assert that it is still visible but switched to the ANIMATING_OUT state - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // assert that it is still visible but switched to the AnimatingOut state + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, batteryChip.contentView.alpha) assertEquals(1f, batteryChip.view.alpha) verify(listener, times(1)).onSystemEventAnimationFinish(false) @@ -149,7 +156,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // skip disappear animation animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) // assert that it is not visible anymore - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) assertEquals(0f, batteryChip.contentView.alpha) assertEquals(0f, batteryChip.view.alpha) } @@ -166,7 +173,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { createAndScheduleFakePrivacyEvent() // THEN the privacy event still happens - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) } @Test @@ -177,12 +184,12 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { val privacyChip = createAndScheduleFakePrivacyEvent() // assert that animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // skip debounce delay advanceTimeBy(DEBOUNCE_DELAY + 1) // status chip starts animating in after debounce delay - assertEquals(ANIMATING_IN, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingIn, systemStatusAnimationScheduler.animationState.value) assertEquals(0f, privacyChip.view.alpha) verify(listener, times(1)).onSystemEventAnimationBegin() @@ -190,13 +197,13 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) advanceTimeBy(APPEAR_ANIMATION_DURATION + 1) // assert that status chip is visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) // skip status chip display time advanceTimeBy(DISPLAY_LENGTH + 1) - // assert that it is still visible but switched to the ANIMATING_OUT state - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // assert that it is still visible but switched to the AnimatingOut state + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) verify(listener, times(1)).onSystemEventAnimationFinish(true) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) @@ -205,13 +212,13 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { advanceTimeBy(DISAPPEAR_ANIMATION_DURATION + 1) animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) // assert that it the dot is now visible - assertEquals(SHOWING_PERSISTENT_DOT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(ShowingPersistentDot, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) // notify SystemStatusAnimationScheduler to remove persistent dot systemStatusAnimationScheduler.removePersistentDot() - // assert that IDLE state is entered - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + // assert that Idle state is entered + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onHidePersistentDot() } @@ -225,19 +232,19 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { batteryChip.view.alpha = 0f // assert that animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // create and schedule high priority event val privacyChip = createAndScheduleFakePrivacyEvent() // assert that animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // skip debounce delay and appear animation duration - fastForwardAnimationToState(RUNNING_CHIP_ANIM) + fastForwardAnimationToState(RunningChipAnim) // high priority status chip is visible while low priority status chip is not visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) assertEquals(0f, batteryChip.view.alpha) } @@ -250,11 +257,11 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule low priority event val batteryChip = createAndScheduleFakeBatteryEvent() - // fast forward to RUNNING_CHIP_ANIM state - fastForwardAnimationToState(RUNNING_CHIP_ANIM) + // fast forward to RunningChipAnim state + fastForwardAnimationToState(RunningChipAnim) // assert that chip is displayed - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, batteryChip.view.alpha) // create and schedule high priority event @@ -264,20 +271,20 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { testScheduler.runCurrent() // assert that currently displayed chip is immediately animated out - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) // skip disappear animation animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) // assert that high priority privacy chip animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // skip debounce delay and appear animation advanceTimeBy(DEBOUNCE_DELAY + APPEAR_ANIMATION_DURATION + 1) animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) // high priority status chip is visible while low priority status chip is not visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) assertEquals(0f, batteryChip.view.alpha) } @@ -294,7 +301,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { advanceTimeBy(DEBOUNCE_DELAY + 1) // assert that chip is animated in - assertEquals(ANIMATING_IN, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingIn, systemStatusAnimationScheduler.animationState.value) // create and schedule high priority event val privacyChip = createAndScheduleFakePrivacyEvent() @@ -303,7 +310,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { testScheduler.runCurrent() // assert that currently animated chip keeps animating - assertEquals(ANIMATING_IN, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingIn, systemStatusAnimationScheduler.animationState.value) // skip appear animation animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) @@ -311,20 +318,20 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // assert that low priority chip is animated out immediately after finishing the appear // animation - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) // skip disappear animation animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) // assert that high priority privacy chip animation is queued - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // skip debounce delay and appear animation advanceTimeBy(DEBOUNCE_DELAY + APPEAR_ANIMATION_DURATION + 1) animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) // high priority status chip is visible while low priority status chip is not visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) assertEquals(0f, batteryChip.view.alpha) } @@ -346,7 +353,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) // high priority status chip is visible while low priority status chip is not visible - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) assertEquals(1f, privacyChip.view.alpha) assertEquals(0f, batteryChip.view.alpha) } @@ -359,14 +366,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule high priority event createAndScheduleFakePrivacyEvent() - // skip chip animation lifecycle and fast forward to SHOWING_PERSISTENT_DOT state - fastForwardAnimationToState(SHOWING_PERSISTENT_DOT) - assertEquals(SHOWING_PERSISTENT_DOT, systemStatusAnimationScheduler.getAnimationState()) + // skip chip animation lifecycle and fast forward to ShowingPersistentDot state + fastForwardAnimationToState(ShowingPersistentDot) + assertEquals(ShowingPersistentDot, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) - // remove persistent dot and verify that animationState changes to IDLE + // remove persistent dot and verify that animationState changes to Idle systemStatusAnimationScheduler.removePersistentDot() - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onHidePersistentDot() } @@ -377,14 +384,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { val accessibilityDesc = "Some desc" val mockView = mock<View>() val mockAnimatableView = - mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) } + mock<BackgroundAnimatableView> { whenever(it.view).thenReturn(mockView) } scheduleFakeEventWithView( accessibilityDesc, mockAnimatableView, shouldAnnounceAccessibilityEvent = true, ) - fastForwardAnimationToState(ANIMATING_OUT) + fastForwardAnimationToState(AnimatingOut) verify(mockView).announceForAccessibility(eq(accessibilityDesc)) } @@ -396,14 +403,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { val accessibilityDesc = null val mockView = mock<View>() val mockAnimatableView = - mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) } + mock<BackgroundAnimatableView> { whenever(it.view).thenReturn(mockView) } scheduleFakeEventWithView( accessibilityDesc, mockAnimatableView, shouldAnnounceAccessibilityEvent = true, ) - fastForwardAnimationToState(ANIMATING_OUT) + fastForwardAnimationToState(AnimatingOut) verify(mockView, never()).announceForAccessibility(any()) } @@ -415,14 +422,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { val accessibilityDesc = "something" val mockView = mock<View>() val mockAnimatableView = - mock<BackgroundAnimatableView> { whenever(view).thenReturn(mockView) } + mock<BackgroundAnimatableView> { whenever(it.view).thenReturn(mockView) } scheduleFakeEventWithView( accessibilityDesc, mockAnimatableView, shouldAnnounceAccessibilityEvent = false, ) - fastForwardAnimationToState(ANIMATING_OUT) + fastForwardAnimationToState(AnimatingOut) verify(mockView, never()).announceForAccessibility(any()) } @@ -435,21 +442,21 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule high priority event createAndScheduleFakePrivacyEvent() - // skip chip animation lifecycle and fast forward to RUNNING_CHIP_ANIM state - fastForwardAnimationToState(RUNNING_CHIP_ANIM) - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) + // skip chip animation lifecycle and fast forward to RunningChipAnim state + fastForwardAnimationToState(RunningChipAnim) + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) // request removal of persistent dot systemStatusAnimationScheduler.removePersistentDot() // skip display time and verify that disappear animation is run advanceTimeBy(DISPLAY_LENGTH + 1) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) - // skip disappear animation and verify that animationState changes to IDLE instead of - // SHOWING_PERSISTENT_DOT + // skip disappear animation and verify that animationState changes to Idle instead of + // ShowingPersistentDot animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) // verify that the persistent dot callbacks are not invoked verify(listener, never()).onSystemStatusAnimationTransitionToPersistentDot(any()) verify(listener, never()).onHidePersistentDot() @@ -463,9 +470,9 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule high priority event createAndScheduleFakePrivacyEvent() - // fast forward to ANIMATING_OUT state - fastForwardAnimationToState(ANIMATING_OUT) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // fast forward to AnimatingOut state + fastForwardAnimationToState(AnimatingOut) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) // remove persistent dot @@ -478,8 +485,8 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) testScheduler.runCurrent() - // verify that animationState changes to IDLE - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + // verify that animationState changes to Idle + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) } @Test @@ -494,11 +501,11 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule a privacy event again (resets forceVisible to true) createAndScheduleFakePrivacyEvent() - // skip chip animation lifecycle and fast forward to SHOWING_PERSISTENT_DOT state - fastForwardAnimationToState(SHOWING_PERSISTENT_DOT) + // skip chip animation lifecycle and fast forward to ShowingPersistentDot state + fastForwardAnimationToState(ShowingPersistentDot) - // verify that we reach SHOWING_PERSISTENT_DOT and that listener callback is invoked - assertEquals(SHOWING_PERSISTENT_DOT, systemStatusAnimationScheduler.getAnimationState()) + // verify that we reach ShowingPersistentDot and that listener callback is invoked + assertEquals(ShowingPersistentDot, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) } @@ -511,21 +518,21 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { createAndScheduleFakePrivacyEvent() // request removal of persistent dot (sets forceVisible to false) systemStatusAnimationScheduler.removePersistentDot() - fastForwardAnimationToState(RUNNING_CHIP_ANIM) + fastForwardAnimationToState(RunningChipAnim) // create and schedule a privacy event again (resets forceVisible to true) createAndScheduleFakePrivacyEvent() // skip status chip display time advanceTimeBy(DISPLAY_LENGTH + 1) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemEventAnimationFinish(anyBoolean()) // skip disappear animation animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) - // verify that we reach SHOWING_PERSISTENT_DOT and that listener callback is invoked - assertEquals(SHOWING_PERSISTENT_DOT, systemStatusAnimationScheduler.getAnimationState()) + // verify that we reach ShowingPersistentDot and that listener callback is invoked + assertEquals(ShowingPersistentDot, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) } @@ -537,9 +544,9 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule high priority event createAndScheduleFakePrivacyEvent() - // skip chip animation lifecycle and fast forward to ANIMATING_OUT state - fastForwardAnimationToState(ANIMATING_OUT) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // skip chip animation lifecycle and fast forward to AnimatingOut state + fastForwardAnimationToState(AnimatingOut) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) // request removal of persistent dot @@ -548,13 +555,13 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // schedule another high priority event while the event is animating out createAndScheduleFakePrivacyEvent() - // verify that the state is still ANIMATING_OUT - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // verify that the state is still AnimatingOut + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) - // skip disappear animation duration and verify that new state is ANIMATION_QUEUED + // skip disappear animation duration and verify that new state is AnimationQueued animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) testScheduler.runCurrent() - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) // also verify that onHidePersistentDot callback is called verify(listener, times(1)).onHidePersistentDot() } @@ -567,16 +574,16 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // create and schedule high priority event createAndScheduleFakePrivacyEvent() - // skip chip animation lifecycle and fast forward to ANIMATING_OUT state - fastForwardAnimationToState(ANIMATING_OUT) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // skip chip animation lifecycle and fast forward to AnimatingOut state + fastForwardAnimationToState(AnimatingOut) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) // request removal of persistent dot systemStatusAnimationScheduler.removePersistentDot() - // verify that the state is still ANIMATING_OUT - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + // verify that the state is still AnimatingOut + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) // skip disappear animation duration testScheduler.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION + 1) @@ -591,33 +598,33 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { // verify that onHidePersistentDot is invoked despite the animator callback being delayed // (it's invoked more than DISAPPEAR_ANIMATION_DURATION after the dot removal was requested) verify(listener, times(1)).onHidePersistentDot() - // verify that animationState is IDLE - assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + // verify that animationState is Idle + assertEquals(Idle, systemStatusAnimationScheduler.animationState.value) } - private fun TestScope.fastForwardAnimationToState(@SystemAnimationState animationState: Int) { + private fun TestScope.fastForwardAnimationToState(animationState: SystemEventAnimationState) { // this function should only be called directly after posting a status event - assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState()) - if (animationState == IDLE || animationState == ANIMATION_QUEUED) return + assertEquals(AnimationQueued, systemStatusAnimationScheduler.animationState.value) + if (animationState == Idle || animationState == AnimationQueued) return // skip debounce delay advanceTimeBy(DEBOUNCE_DELAY + 1) // status chip starts animating in after debounce delay - assertEquals(ANIMATING_IN, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingIn, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemEventAnimationBegin() - if (animationState == ANIMATING_IN) return + if (animationState == AnimatingIn) return // skip appear animation animatorTestRule.advanceTimeBy(APPEAR_ANIMATION_DURATION) advanceTimeBy(APPEAR_ANIMATION_DURATION) - assertEquals(RUNNING_CHIP_ANIM, systemStatusAnimationScheduler.getAnimationState()) - if (animationState == RUNNING_CHIP_ANIM) return + assertEquals(RunningChipAnim, systemStatusAnimationScheduler.animationState.value) + if (animationState == RunningChipAnim) return // skip status chip display time advanceTimeBy(DISPLAY_LENGTH + 1) - assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + assertEquals(AnimatingOut, systemStatusAnimationScheduler.animationState.value) verify(listener, times(1)).onSystemEventAnimationFinish(anyBoolean()) - if (animationState == ANIMATING_OUT) return + if (animationState == AnimatingOut) return // skip disappear animation animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index 2cf599a99c63..e21a005b8ada 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -45,6 +45,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -63,6 +64,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.os.Handler; import android.os.RemoteException; +import android.platform.test.annotations.EnableFlags; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; @@ -70,13 +72,13 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.Pair; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.LogBufferEulogizer; @@ -129,6 +131,7 @@ public class NotifCollectionTest extends SysuiTestCase { @Mock private GroupCoalescer mGroupCoalescer; @Spy private RecordingCollectionListener mCollectionListener; @Mock private CollectionReadyForBuildListener mBuildListener; + @Mock private NotificationDismissibilityProvider mDismissibilityProvider; @Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1"); @Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2"); @@ -160,6 +163,7 @@ public class NotifCollectionTest extends SysuiTestCase { allowTestableLooperAsMainThread(); when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]); + doReturn(Boolean.TRUE).when(mDismissibilityProvider).isDismissable(any()); mListenerInOrder = inOrder(mCollectionListener); @@ -172,7 +176,7 @@ public class NotifCollectionTest extends SysuiTestCase { mBgExecutor, mEulogizer, mock(DumpManager.class), - mock(NotificationDismissibilityProvider.class)); + mDismissibilityProvider); mCollection.attach(mGroupCoalescer); mCollection.addCollectionListener(mCollectionListener); mCollection.setBuildListener(mBuildListener); @@ -1287,8 +1291,8 @@ public class NotifCollectionTest extends SysuiTestCase { // WHEN both notifications are manually dismissed together mCollection.dismissNotifications( - List.of(new Pair<>(entry1, defaultStats(entry1)), - new Pair<>(entry2, defaultStats(entry2)))); + List.of(entryWithDefaultStats(entry1), + entryWithDefaultStats(entry2))); // THEN build list is only called one time verifyBuiltList(List.of(entry1, entry2)); @@ -1306,8 +1310,8 @@ public class NotifCollectionTest extends SysuiTestCase { DismissedByUserStats stats1 = defaultStats(entry1); DismissedByUserStats stats2 = defaultStats(entry2); mCollection.dismissNotifications( - List.of(new Pair<>(entry1, defaultStats(entry1)), - new Pair<>(entry2, defaultStats(entry2)))); + List.of(entryWithDefaultStats(entry1), + entryWithDefaultStats(entry2))); // THEN we send the dismissals to system server FakeExecutor.exhaustExecutors(mBgExecutor); @@ -1338,8 +1342,8 @@ public class NotifCollectionTest extends SysuiTestCase { // WHEN both notifications are manually dismissed together mCollection.dismissNotifications( - List.of(new Pair<>(entry1, defaultStats(entry1)), - new Pair<>(entry2, defaultStats(entry2)))); + List.of(entryWithDefaultStats(entry1), + entryWithDefaultStats(entry2))); // THEN the entries are marked as dismissed assertEquals(DISMISSED, entry1.getDismissState()); @@ -1363,8 +1367,8 @@ public class NotifCollectionTest extends SysuiTestCase { // WHEN both notifications are manually dismissed together mCollection.dismissNotifications( - List.of(new Pair<>(entry1, defaultStats(entry1)), - new Pair<>(entry2, defaultStats(entry2)))); + List.of(entryWithDefaultStats(entry1), + entryWithDefaultStats(entry2))); // THEN all interceptors get checked verify(mInterceptor1).shouldInterceptDismissal(entry1); @@ -1379,6 +1383,43 @@ public class NotifCollectionTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_NOTIFICATIONS_DISMISS_PRUNED_SUMMARIES) + public void testDismissNotificationsIncludesPrunedParents() { + // GIVEN a collection with 2 groups; one has a single child, one has two. + mCollection.addNotificationDismissInterceptor(mInterceptor1); + + NotifEvent notif1summary = mNoMan.postNotif( + buildNotif(TEST_PACKAGE, 1, "notif1summary").setGroup(mContext, "group1") + .setGroupSummary(mContext, true)); + NotifEvent notif1child = mNoMan.postNotif( + buildNotif(TEST_PACKAGE, 1, "notif1child").setGroup(mContext, "group1")); + NotifEvent notif2summary = mNoMan.postNotif( + buildNotif(TEST_PACKAGE2, 2, "notif2summary").setGroup(mContext, "group2") + .setGroupSummary(mContext, true)); + NotifEvent notif2child1 = mNoMan.postNotif( + buildNotif(TEST_PACKAGE2, 2, "notif2child1").setGroup(mContext, "group2")); + NotifEvent notif2child2 = mNoMan.postNotif( + buildNotif(TEST_PACKAGE2, 2, "notif2child2").setGroup(mContext, "group2")); + NotificationEntry entry1summary = mCollectionListener.getEntry(notif1summary.key); + NotificationEntry entry1child = mCollectionListener.getEntry(notif1child.key); + NotificationEntry entry2summary = mCollectionListener.getEntry(notif2summary.key); + NotificationEntry entry2child1 = mCollectionListener.getEntry(notif2child1.key); + NotificationEntry entry2child2 = mCollectionListener.getEntry(notif2child2.key); + + // WHEN one child from each group are manually dismissed together + mCollection.dismissNotifications( + List.of(entryWithDefaultStats(entry1child), + entryWithDefaultStats(entry2child1))); + + // THEN the summary for the singleton child is dismissed, but not the other summary + verify(mInterceptor1).shouldInterceptDismissal(entry1summary); + verify(mInterceptor1).shouldInterceptDismissal(entry1child); + verify(mInterceptor1, never()).shouldInterceptDismissal(entry2summary); + verify(mInterceptor1).shouldInterceptDismissal(entry2child1); + verify(mInterceptor1, never()).shouldInterceptDismissal(entry2child2); + } + + @Test public void testDismissAllNotificationsCallsRebuildOnce() { // GIVEN a collection with a couple notifications NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag")); @@ -1764,6 +1805,10 @@ public class NotifCollectionTest extends SysuiTestCase { NotificationVisibility.obtain(entry.getKey(), 7, 2, true)); } + private static EntryWithDismissStats entryWithDefaultStats(NotificationEntry entry) { + return new EntryWithDismissStats(entry, defaultStats(entry)); + } + private CollectionEvent postNotif(NotificationEntryBuilder builder) { clearInvocations(mCollectionListener); NotifEvent rawEvent = mNoMan.postNotif(builder); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java index 72d1db3affe8..31c650d861ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; import android.app.PendingIntent; import android.app.Person; import android.content.Intent; @@ -372,6 +373,29 @@ public class NotificationEntryTest extends SysuiTestCase { } @Test + public void notificationDataEntry_testIsLastMessageFromReply_invalidData() { + Bundle bundle = new Bundle(); + bundle.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, + new NotificationChannelGroup[]{}); + + Notification notification = new Notification.Builder(mContext, "test") + .addExtras(bundle) + .build(); + + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg("pkg") + .setOpPkg("pkg") + .setTag("tag") + .setNotification(notification) + .setUser(mContext.getUser()) + .setOverrideGroupKey("") + .build(); + entry.setHasSentReply(); + + assertFalse(entry.isLastMessageFromReply()); + } + + @Test public void notificationDataEntry_testIsLastMessageFromReply_invalidPerson_noCrash() { Person.Builder person = new Person.Builder() .setName("name") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 0b5f8d5e948c..723c0d701305 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -74,22 +74,31 @@ import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager import java.util.Optional -import junit.framework.Assert import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runCurrent +import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.invocation.InvocationOnMock +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever /** Tests for [NotificationGutsManager] with the scene container enabled. */ +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -99,7 +108,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { NotificationChannel( TEST_CHANNEL_ID, TEST_CHANNEL_ID, - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_DEFAULT, ) private val kosmos = testKosmos() @@ -146,7 +155,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) allowTestableLooperAsMainThread() helper = NotificationTestHelper(mContext, mDependency) - Mockito.`when`(accessibilityManager.isTouchExplorationEnabled).thenReturn(false) + whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(false) windowRootViewVisibilityInteractor = WindowRootViewVisibilityInteractor( testScope.backgroundScope, @@ -185,12 +194,12 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { deviceProvisionedController, metricsLogger, headsUpManager, - activityStarter + activityStarter, ) gutsManager.setUpWithPresenter( presenter, notificationListContainer, - onSettingsClickListener + onSettingsClickListener, ) gutsManager.setNotificationActivityStarter(notificationActivityStarter) gutsManager.start() @@ -198,49 +207,31 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { @Test fun testOpenAndCloseGuts() { - val guts = Mockito.spy(NotificationGuts(mContext)) - Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock - -> + val guts = spy(NotificationGuts(mContext)) + whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock -> handler.post((invocation.arguments[0] as Runnable)) null } // Test doesn't support animation since the guts view is not attached. - Mockito.doNothing() - .`when`(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + doNothing() + .whenever(guts) + .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) val realRow = createTestNotificationRow() val menuItem = createTestMenuItem(realRow) - val row = Mockito.spy(realRow) - Mockito.`when`(row!!.windowToken).thenReturn(Binder()) - Mockito.`when`(row.guts).thenReturn(guts) + val row = spy(realRow) + whenever(row!!.windowToken).thenReturn(Binder()) + whenever(row.guts).thenReturn(guts) Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem)) assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong()) executor.runAllReady() - verify(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) verify(headsUpManager).setGutsShown(realRow!!.entry, true) assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong()) gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false) verify(guts) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) - verify(row, Mockito.times(1)).setGutsView(ArgumentMatchers.any()) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) + verify(row, times(1)).setGutsView(any()) executor.runAllReady() verify(headsUpManager).setGutsShown(realRow.entry, false) } @@ -250,7 +241,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { // First, start out lockscreen or shade as not visible setIsLockscreenOrShadeVisible(false) testScope.testScheduler.runCurrent() - val guts = Mockito.mock(NotificationGuts::class.java) + val guts = mock<NotificationGuts>() gutsManager.exposedGuts = guts // WHEN the lockscreen or shade becomes visible @@ -258,15 +249,9 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the guts are not closed - verify(guts, Mockito.never()).removeCallbacks(ArgumentMatchers.any()) - verify(guts, Mockito.never()) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) + verify(guts, never()).removeCallbacks(any()) + verify(guts, never()) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) } @Test @@ -274,7 +259,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { // First, start out lockscreen or shade as visible setIsLockscreenOrShadeVisible(true) testScope.testScheduler.runCurrent() - val guts = Mockito.mock(NotificationGuts::class.java) + val guts = mock<NotificationGuts>() gutsManager.exposedGuts = guts // WHEN the lockscreen or shade is no longer visible @@ -282,14 +267,14 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the guts are closed - verify(guts).removeCallbacks(ArgumentMatchers.any()) + verify(guts).removeCallbacks(anyOrNull()) verify(guts) .closeControls( - /* leavebehinds= */ ArgumentMatchers.eq(true), - /* controls= */ ArgumentMatchers.eq(true), - /* x= */ ArgumentMatchers.anyInt(), - /* y= */ ArgumentMatchers.anyInt(), - /* force= */ ArgumentMatchers.eq(true) + /* leavebehinds= */ eq(true), + /* controls= */ eq(true), + /* x= */ any<Int>(), + /* y= */ any<Int>(), + /* force= */ eq(true), ) } @@ -304,95 +289,68 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { testScope.testScheduler.runCurrent() // THEN the list container is reset - verify(notificationListContainer) - .resetExposedMenuView(ArgumentMatchers.anyBoolean(), ArgumentMatchers.anyBoolean()) + verify(notificationListContainer).resetExposedMenuView(any<Boolean>(), any<Boolean>()) } @Test fun testChangeDensityOrFontScale() { - val guts = Mockito.spy(NotificationGuts(mContext)) - Mockito.`when`(guts.post(ArgumentMatchers.any())).thenAnswer { invocation: InvocationOnMock - -> + val guts = spy(NotificationGuts(mContext)) + whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock -> handler.post((invocation.arguments[0] as Runnable)) null } // Test doesn't support animation since the guts view is not attached. - Mockito.doNothing() - .`when`(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + doNothing() + .whenever(guts) + .openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) val realRow = createTestNotificationRow() val menuItem = createTestMenuItem(realRow) - val row = Mockito.spy(realRow) - Mockito.`when`(row!!.windowToken).thenReturn(Binder()) - Mockito.`when`(row.guts).thenReturn(guts) - Mockito.doNothing().`when`(row).ensureGutsInflated() + val row = spy(realRow) + whenever(row!!.windowToken).thenReturn(Binder()) + whenever(row.guts).thenReturn(guts) + doNothing().whenever(row).ensureGutsInflated() val realEntry = realRow!!.entry - val entry = Mockito.spy(realEntry) - Mockito.`when`(entry.row).thenReturn(row) - Mockito.`when`(entry.getGuts()).thenReturn(guts) + val entry = spy(realEntry) + whenever(entry.row).thenReturn(row) + whenever(entry.getGuts()).thenReturn(guts) Assert.assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem)) executor.runAllReady() - verify(guts) - .openControls( - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.any(Runnable::class.java) - ) + verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) // called once by mGutsManager.bindGuts() in mGutsManager.openGuts() - verify(row).setGutsView(ArgumentMatchers.any()) + verify(row).setGutsView(any()) row.onDensityOrFontScaleChanged() gutsManager.onDensityOrFontScaleChanged(entry) executor.runAllReady() gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false) verify(guts) - .closeControls( - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyBoolean() - ) + .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged() - verify(row, Mockito.times(2)).setGutsView(ArgumentMatchers.any()) + verify(row, times(2)).setGutsView(any()) } @Test fun testAppOpsSettingsIntent_camera() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test fun testAppOpsSettingsIntent_mic() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_RECORD_AUDIO) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test @@ -400,30 +358,22 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_RECORD_AUDIO) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Intent.ACTION_MANAGE_APP_PERMISSIONS, captor.lastValue.action) } @Test fun testAppOpsSettingsIntent_overlay() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION, captor.lastValue.action) } @Test @@ -432,15 +382,11 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_RECORD_AUDIO) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @@ -448,15 +394,11 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_CAMERA) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @@ -464,112 +406,108 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { val ops = ArraySet<Int>() ops.add(AppOpsManager.OP_RECORD_AUDIO) ops.add(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - gutsManager.startAppOpsSettingsActivity("", 0, ops, null) - val captor = ArgumentCaptor.forClass(Intent::class.java) - verify(notificationActivityStarter, Mockito.times(1)) - .startNotificationGutsIntent( - captor.capture(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any() - ) - assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.value.action) + gutsManager.startAppOpsSettingsActivity("", 0, ops, mock<ExpandableNotificationRow>()) + val captor = argumentCaptor<Intent>() + verify(notificationActivityStarter, times(1)) + .startNotificationGutsIntent(captor.capture(), any<Int>(), any()) + assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.lastValue.action) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_highPriority() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) val entry = row.entry NotificationEntryHelper.modifyRanking(entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .setImportance(NotificationManager.IMPORTANCE_HIGH) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) - Mockito.`when`(highPriorityProvider.isHighPriority(entry)).thenReturn(true) + whenever(row.getIsNonblockable()).thenReturn(false) + whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true) val statusBarNotification = entry.sbn gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(true), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(true), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_PassesAlongProvisionedState() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) + whenever(row.getIsNonblockable()).thenReturn(false) val statusBarNotification = row.entry.sbn val entry = row.entry gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(false), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(false), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } @Test @Throws(Exception::class) fun testInitializeNotificationInfoView_withInitialAction() { - val notificationInfoView = Mockito.mock(NotificationInfo::class.java) - val row = Mockito.spy(helper.createRow()) + val notificationInfoView = mock<NotificationInfo>() + val row = spy(helper.createRow()) NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .build() - Mockito.`when`(row.getIsNonblockable()).thenReturn(false) + whenever(row.getIsNonblockable()).thenReturn(false) val statusBarNotification = row.entry.sbn val entry = row.entry gutsManager.initializeNotificationInfo(row, notificationInfoView) verify(notificationInfoView) .bindNotification( - ArgumentMatchers.any(PackageManager::class.java), - ArgumentMatchers.any(INotificationManager::class.java), - ArgumentMatchers.eq(onUserInteractionCallback), - ArgumentMatchers.eq(channelEditorDialogController), - ArgumentMatchers.eq(statusBarNotification.packageName), - ArgumentMatchers.any(NotificationChannel::class.java), - ArgumentMatchers.eq(entry), - ArgumentMatchers.any(NotificationInfo.OnSettingsClickListener::class.java), - ArgumentMatchers.any(NotificationInfo.OnAppSettingsClickListener::class.java), - ArgumentMatchers.any(UiEventLogger::class.java), - ArgumentMatchers.eq(true), - ArgumentMatchers.eq(false), - ArgumentMatchers.eq(false), /* wasShownHighPriority */ - ArgumentMatchers.eq(assistantFeedbackController), - ArgumentMatchers.any(MetricsLogger::class.java) + any<PackageManager>(), + any<INotificationManager>(), + eq(onUserInteractionCallback), + eq(channelEditorDialogController), + eq(statusBarNotification.packageName), + any<NotificationChannel>(), + eq(entry), + any<NotificationInfo.OnSettingsClickListener>(), + any<NotificationInfo.OnAppSettingsClickListener>(), + any<UiEventLogger>(), + eq(true), + eq(false), + eq(false), /* wasShownHighPriority */ + eq(assistantFeedbackController), + any<MetricsLogger>(), ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 87cda64ed8e5..fdfc25390a3f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -244,7 +244,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat())) .thenReturn((float) stackHeight); - mStackScroller.updateStackHeight(); + mStackScroller.updateIntrinsicStackHeight(); assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeight); } @@ -1218,7 +1218,38 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @DisableSceneContainer // TODO(b/312473478): address disabled test + @EnableSceneContainer + public void testSetMaxDisplayedNotifications_updatesStackHeight() { + int fullStackHeight = 300; + int limitedStackHeight = 100; + int maxNotifs = 2; // any non-zero limit + float stackTop = 100; + float stackCutoff = 1100; + float stackViewPortHeight = stackCutoff - stackTop; + mStackScroller.setStackTop(stackTop); + mStackScroller.setStackCutoff(stackCutoff); + when(mStackSizeCalculator.computeHeight(eq(mStackScroller), eq(-1), anyFloat())) + .thenReturn((float) fullStackHeight); + when(mStackSizeCalculator.computeHeight(eq(mStackScroller), eq(maxNotifs), anyFloat())) + .thenReturn((float) limitedStackHeight); + + // When we set a limit on max displayed notifications + mStackScroller.setMaxDisplayedNotifications(maxNotifs); + + // Then + assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(limitedStackHeight); + assertThat(mAmbientState.getStackEndHeight()).isEqualTo(limitedStackHeight); + + // When there is no limit on max displayed notifications + mStackScroller.setMaxDisplayedNotifications(-1); + + // Then + assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(fullStackHeight); + assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackViewPortHeight); + } + + @Test + @DisableSceneContainer public void testSetMaxDisplayedNotifications_notifiesListeners() { ExpandableView.OnHeightChangedListener listener = mock(ExpandableView.OnHeightChangedListener.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 1e88215bf339..f472fd1019eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -172,9 +172,9 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarInitializerImpl; import com.android.systemui.statusbar.core.StatusBarOrchestrator; -import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -1135,7 +1135,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @DisableFlags(StatusBarSimpleFragment.FLAG_NAME) + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) public void bubbleBarVisibility() { createCentralSurfaces(); mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 83d0bcc55cb0..008e8ce92736 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -37,7 +37,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.battery.BatteryMeterView import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.fakeDarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.scene.ui.view.WindowRootView @@ -46,9 +45,12 @@ import com.android.systemui.shade.ShadeLogger import com.android.systemui.shade.ShadeViewController import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore +import com.android.systemui.statusbar.data.repository.statusBarContentInsetsProviderStore import com.android.systemui.statusbar.policy.Clock import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.testKosmos import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider @@ -75,7 +77,9 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class PhoneStatusBarViewControllerTest : SysuiTestCase() { - private val kosmos = Kosmos() + private val kosmos = testKosmos() + private val statusBarContentInsetsProviderStore = kosmos.fakeStatusBarContentInsetsProviderStore + private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher @Mock private lateinit var shadeViewController: ShadeViewController @@ -93,7 +97,6 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { @Mock private lateinit var windowRootView: Provider<WindowRootView> @Mock private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var viewUtil: ViewUtil - @Mock private lateinit var statusBarContentInsetsProvider: StatusBarContentInsetsProvider private lateinit var statusBarWindowStateController: StatusBarWindowStateController private lateinit var view: PhoneStatusBarView @@ -396,7 +399,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { configurationController, mStatusOverlayHoverListenerFactory, fakeDarkIconDispatcher, - statusBarContentInsetsProvider, + statusBarContentInsetsProviderStore, ) .create(view) .also { it.init() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index d01c1ca36c4e..e26a7ea26545 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; @@ -60,7 +61,6 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; -import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -633,7 +633,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, - StatusBarNotifChips.FLAG_NAME, + FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() { resumeAndGetFragment(); @@ -660,8 +660,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() { resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -673,7 +673,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -689,8 +689,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -705,9 +705,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() { + public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -724,8 +724,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -740,9 +740,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() { + public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -759,8 +759,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() { resumeAndGetFragment(); // Ongoing activity started @@ -781,9 +781,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() { + public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() { resumeAndGetFragment(); // Ongoing activity started @@ -804,7 +804,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void secondaryOngoingActivityEnded_chipHidden() { resumeAndGetFragment(); @@ -828,8 +828,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -846,9 +846,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() { + public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -866,8 +866,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() { + @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback @@ -898,9 +898,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() { + public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/CameraProtectionLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CameraProtectionLoaderKosmos.kt new file mode 100644 index 000000000000..7625049594a4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CameraProtectionLoaderKosmos.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 + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeCameraProtectionLoaderFactory by + Kosmos.Fixture { FakeCameraProtectionLoaderFactory() } + +var Kosmos.cameraProtectionLoaderFactory: CameraProtectionLoaderImpl.Factory by + Kosmos.Fixture { fakeCameraProtectionLoaderFactory } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeCameraProtectionLoaderFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeCameraProtectionLoaderFactory.kt new file mode 100644 index 000000000000..87428b0679ba --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeCameraProtectionLoaderFactory.kt @@ -0,0 +1,27 @@ +/* + * 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 + +import android.content.Context +import org.mockito.kotlin.mock + +class FakeCameraProtectionLoaderFactory : CameraProtectionLoaderImpl.Factory { + + override fun create(context: Context): CameraProtectionLoaderImpl { + return mock<CameraProtectionLoaderImpl>() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSysUICutoutProviderFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSysUICutoutProviderFactory.kt new file mode 100644 index 000000000000..4eb3780d7663 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSysUICutoutProviderFactory.kt @@ -0,0 +1,30 @@ +/* + * 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 + +import android.content.Context +import org.mockito.kotlin.mock + +class FakeSysUICutoutProviderFactory : SysUICutoutProviderImpl.Factory { + + override fun create( + context: Context, + cameraProtectionLoader: CameraProtectionLoader, + ): SysUICutoutProviderImpl { + return mock<SysUICutoutProviderImpl>() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUICutoutProviderKosmos.kt index daf6087c2d6f..412ed72e89f7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUICutoutProviderKosmos.kt @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.systemui.qs.panels.ui.viewmodel +package com.android.systemui import com.android.systemui.kosmos.Kosmos -import com.android.systemui.qs.panels.domain.interactor.iconLabelVisibilityInteractor -val Kosmos.iconLabelVisibilityViewModel by - Kosmos.Fixture { IconLabelVisibilityViewModelImpl(iconLabelVisibilityInteractor) } +val Kosmos.fakeSysUICutoutProviderFactory by Kosmos.Fixture { FakeSysUICutoutProviderFactory() } + +var Kosmos.sysUICutoutProviderFactory: SysUICutoutProviderImpl.Factory by + Kosmos.Fixture { fakeSysUICutoutProviderFactory } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt index 3fd2503096b5..93e7f2e588b0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.education.domain.interactor import android.hardware.input.InputManager +import com.android.systemui.education.ContextualEducationMetricsLogger import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository import com.android.systemui.inputdevice.tutorial.tutorialSchedulerRepository @@ -41,11 +42,13 @@ var Kosmos.keyboardTouchpadEduInteractor by touchpadRepository, userRepository, ), - tutorialSchedulerRepository, - mockOverviewProxyService, + tutorialRepository = tutorialSchedulerRepository, + overviewProxyService = mockOverviewProxyService, + metricsLogger = mockEduMetricsLogger, clock = fakeEduClock, ) } +var Kosmos.mockEduMetricsLogger by Kosmos.Fixture { mock<ContextualEducationMetricsLogger>() } var Kosmos.mockOverviewProxyService by Kosmos.Fixture { mock<OverviewProxyService>() } var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/TileHapticsViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/TileHapticsViewModelFactoryKosmos.kt new file mode 100644 index 000000000000..a798eb746943 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/TileHapticsViewModelFactoryKosmos.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.haptics.msdl + +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModel +import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel + +val Kosmos.tileHapticsViewModelFactory by + Kosmos.Fixture { + object : TileHapticsViewModel.Factory { + override fun create(tileViewModel: TileViewModel): TileHapticsViewModel = + TileHapticsViewModel(fakeMSDLPlayer, tileViewModel) + } + } + +val Kosmos.tileHapticsViewModelFactoryProvider by + Kosmos.Fixture { TileHapticsViewModelFactoryProvider(tileHapticsViewModelFactory) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 08786495eca4..693ec7954d70 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -131,6 +131,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { override val shortcutAbsoluteTop: StateFlow<Float> get() = _shortcutAbsoluteTop.asStateFlow() + private val _notificationStackAbsoluteBottom = MutableStateFlow(0F) + override val notificationStackAbsoluteBottom: StateFlow<Float> + get() = _notificationStackAbsoluteBottom.asStateFlow() + private val _isKeyguardEnabled = MutableStateFlow(true) override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow() @@ -294,6 +298,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _shortcutAbsoluteTop.value = top } + override fun setNotificationStackAbsoluteBottom(bottom: Float) { + _notificationStackAbsoluteBottom.value = bottom + } + override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) { _canIgnoreAuthAndReturnToGone.value = canWake } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index 70b4f79131a7..4976cc26068a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.annotation.FloatRange +import com.android.systemui.Flags.transitionRaceCondition import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionInfo @@ -88,6 +89,13 @@ class FakeKeyguardTransitionRepository( ) ) override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow() + override var currentTransitionInfo = + TransitionInfo( + ownerName = "", + from = KeyguardState.OFF, + to = KeyguardState.LOCKSCREEN, + animator = null, + ) init { // Seed with a FINISHED transition in OFF, same as the real repository. @@ -261,8 +269,13 @@ class FakeKeyguardTransitionRepository( validateStep: Boolean = true, ) { if (step.transitionState == TransitionState.STARTED) { - _currentTransitionInfo.value = - TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "") + if (transitionRaceCondition()) { + currentTransitionInfo = + TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "") + } else { + _currentTransitionInfo.value = + TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "") + } } _transitions.replayCache.last().let { lastStep -> @@ -308,7 +321,11 @@ class FakeKeyguardTransitionRepository( } override suspend fun startTransition(info: TransitionInfo): UUID? { - _currentTransitionInfo.value = info + if (transitionRaceCondition()) { + currentTransitionInfo = info + } else { + _currentTransitionInfo.value = info + } if (sendTransitionStepsOnStartTransition) { sendTransitionSteps(from = info.from, to = info.to, testScope = testScope) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index 9593dfbbce29..8c4ec4c2cb75 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -28,8 +28,6 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.shade.data.repository.FakeShadeRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor -import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor.ConfigurationBasedDimensions import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope @@ -56,7 +54,6 @@ object KeyguardInteractorFactory { fromGoneTransitionInteractor: FromGoneTransitionInteractor = mock(), fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor = mock(), fromOccludedTransitionInteractor: FromOccludedTransitionInteractor = mock(), - sharedNotificationContainerInteractor: SharedNotificationContainerInteractor? = null, powerInteractor: PowerInteractor = PowerInteractorFactory.create().powerInteractor, testScope: CoroutineScope = TestScope(), ): WithDependencies { @@ -69,23 +66,6 @@ object KeyguardInteractorFactory { whenever(it.transitionState).thenReturn(transitionStateFlow) whenever(it.isFinishedIn(any(), any())).thenReturn(MutableStateFlow(false)) } - val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>() - configurationDimensionFlow.tryEmit( - ConfigurationBasedDimensions( - useSplitShade = false, - useLargeScreenHeader = false, - marginHorizontal = 0, - marginBottom = 0, - marginTop = 0, - marginTopLargeScreen = 0, - keyguardSplitShadeTopMargin = 0, - ) - ) - val sncInteractor = - sharedNotificationContainerInteractor - ?: mock<SharedNotificationContainerInteractor>().also { - whenever(it.configurationBasedDimensions).thenReturn(configurationDimensionFlow) - } return WithDependencies( repository = repository, featureFlags = featureFlags, @@ -104,7 +84,6 @@ object KeyguardInteractorFactory { fromGoneTransitionInteractor = { fromGoneTransitionInteractor }, fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor }, fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor }, - sharedNotificationContainerInteractor = { sncInteractor }, applicationScope = testScope, ), ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt index e85114d04825..da261bfb9805 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt @@ -24,7 +24,6 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.data.repository.shadeRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor val Kosmos.keyguardInteractor: KeyguardInteractor by Kosmos.Fixture { @@ -39,7 +38,6 @@ val Kosmos.keyguardInteractor: KeyguardInteractor by fromGoneTransitionInteractor = { fromGoneTransitionInteractor }, fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor }, fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor }, - sharedNotificationContainerInteractor = { sharedNotificationContainerInteractor }, applicationScope = testScope.backgroundScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt index b45120ee0aba..43eb93e4dd53 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt @@ -43,30 +43,36 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum private var state = VolumeDialogController.State() override fun setActiveStream(stream: Int) { - // ensure streamState existence for the active stream - state.states.getOrElse(stream) { - VolumeDialogController.StreamState().also { streamState -> - state.states.put(stream, streamState) + updateState { + // ensure streamState existence for the active stream` + states.getOrElse(stream) { + VolumeDialogController.StreamState().also { streamState -> + state.states.put(stream, streamState) + } } + activeStream = stream } - state.activeStream = stream } override fun setStreamVolume(stream: Int, userLevel: Int) { - val streamState = - state.states.getOrElse(stream) { - VolumeDialogController.StreamState().also { streamState -> - state.states.put(stream, streamState) + updateState { + val streamState = + states.getOrElse(stream) { + VolumeDialogController.StreamState().also { streamState -> + states.put(stream, streamState) + } } - } - streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax) + streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax) + } } override fun setRingerMode(ringerModeNormal: Int, external: Boolean) { - if (external) { - state.ringerModeExternal = ringerModeNormal - } else { - state.ringerModeInternal = ringerModeNormal + updateState { + if (external) { + ringerModeExternal = ringerModeNormal + } else { + ringerModeInternal = ringerModeNormal + } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt index 093ebd6c6b63..562980d43485 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt @@ -20,13 +20,10 @@ import com.android.internal.logging.InstanceId import com.android.systemui.animation.Expandable import com.android.systemui.plugins.qs.QSTile -class FakeQSTile( - var user: Int, - var available: Boolean = true, -) : QSTile { +class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile { private var tileSpec: String? = null var destroyed = false - private val state = QSTile.State() + private var state = QSTile.State() val callbacks = mutableListOf<QSTile.Callback>() override fun getTileSpec(): String? { @@ -93,4 +90,9 @@ class FakeQSTile( override fun isListening(): Boolean { return false } + + fun changeState(newState: QSTile.State) { + state = newState + callbacks.forEach { it.onStateChanged(state) } + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt index b6b0a4168c5d..b5a6bf126719 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt @@ -16,10 +16,17 @@ package com.android.systemui.qs.panels.domain.interactor +import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory val Kosmos.infiniteGridLayout by - Kosmos.Fixture { InfiniteGridLayout(iconTilesViewModel, infiniteGridViewModelFactory) } + Kosmos.Fixture { + InfiniteGridLayout( + iconTilesViewModel, + infiniteGridViewModelFactory, + tileHapticsViewModelFactoryProvider, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt index 10d8e1e22487..48ef57e9a62c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt @@ -25,7 +25,6 @@ val Kosmos.paginatedGridViewModel by PaginatedGridViewModel( iconTilesViewModel, qsColumnsViewModel, - iconLabelVisibilityViewModel, paginatedGridInteractor, applicationCoroutineScope, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt index 67d9e0ed552b..41ee260fd5dd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.panels.ui.viewmodel +import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.qs.panels.domain.interactor.quickQuickSettingsRowInteractor @@ -30,5 +31,6 @@ val Kosmos.quickQuickSettingsViewModel by tileSquishinessViewModel, iconTilesViewModel, applicationCoroutineScope, + tileHapticsViewModelFactoryProvider, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModelKosmos.kt new file mode 100644 index 000000000000..223755d7636d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModelKosmos.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.qs.panels.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.FakeQSTile +import com.android.systemui.qs.pipeline.shared.TileSpec + +val Kosmos.fakeQsTile by Kosmos.Fixture { FakeQSTile(user = 0, available = true) } +val Kosmos.tileViewModel by + Kosmos.Fixture { TileViewModel(fakeQsTile, TileSpec.Companion.create("test")) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt index 60141c60a265..6944e6c14096 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt @@ -187,6 +187,7 @@ class ShadeTestUtilLegacyImpl( context .getOrCreateTestableResources() .addOverride(R.bool.config_use_split_notification_shade, splitShade) + shadeRepository.setShadeLayoutWide(splitShade) testScope.runCurrent() } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt index 92075ea75c4a..39f58aea82ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt @@ -26,7 +26,6 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.ShadeModule import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository -import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor import com.android.systemui.statusbar.phone.dozeParameters import com.android.systemui.statusbar.policy.data.repository.userSetupRepository import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor @@ -52,7 +51,6 @@ val Kosmos.shadeInteractorLegacyImpl by ShadeInteractorLegacyImpl( scope = applicationCoroutineScope, keyguardRepository = keyguardRepository, - sharedNotificationContainerInteractor = sharedNotificationContainerInteractor, repository = shadeRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt index a1f157f13210..10534a02bfe7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt @@ -40,7 +40,7 @@ val Kosmos.shadeExpansionStateManager by Fixture { ShadeExpansionStateManager() val Kosmos.shadeStartable by Fixture { ShadeStartable( applicationScope = applicationCoroutineScope, - applicationContext = applicationContext, + context = applicationContext, touchLog = mock<LogBuffer>(), configurationRepository = configurationRepository, shadeRepository = shadeRepository, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt index 2316a2fdcd2b..c0d65a076ca0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel import android.content.packageManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.util.time.fakeSystemClock -val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by +val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by Kosmos.Fixture { - DemoNotifChipViewModel( + DemoRonChipViewModel( commandRegistry = commandRegistry, packageManager = packageManager, systemClock = fakeSystemClock, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/ui/viewmodel/NotifChipsViewModelKosmos.kt index af24c371d62b..11d1cb7e56c8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/ui/viewmodel/NotifChipsViewModelKosmos.kt @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.notification.ui.viewmodel +package com.android.systemui.statusbar.chips.ron.ui.viewmodel import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor val Kosmos.notifChipsViewModel: NotifChipsViewModel by diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 0300bf4636ea..b2be0b21964b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -20,8 +20,8 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel -import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel +import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.ron.ui.viewmodel.notifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel import com.android.systemui.statusbar.chips.statusBarChipsLogger @@ -35,7 +35,7 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, notifChipsViewModel = notifChipsViewModel, - demoNotifChipViewModel = demoNotifChipViewModel, + demoRonChipViewModel = demoRonChipViewModel, logger = statusBarChipsLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarConfigurationControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarConfigurationControllerStore.kt new file mode 100644 index 000000000000..46bb2c169731 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarConfigurationControllerStore.kt @@ -0,0 +1,32 @@ +/* + * 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.statusbar.data.repository + +import android.view.Display +import org.mockito.kotlin.mock + +class FakeStatusBarConfigurationControllerStore : StatusBarConfigurationControllerStore { + + private val perDisplayMockControllers = mutableMapOf<Int, StatusBarConfigurationController>() + + override val defaultDisplay: StatusBarConfigurationController + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): StatusBarConfigurationController { + return perDisplayMockControllers.computeIfAbsent(displayId) { mock() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt new file mode 100644 index 000000000000..642c2ff38338 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.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.statusbar.data.repository + +import android.view.Display +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import org.mockito.kotlin.mock + +class FakeStatusBarContentInsetsProviderStore() : StatusBarContentInsetsProviderStore { + + private val perDisplayMockProviders = mutableMapOf<Int, StatusBarContentInsetsProvider>() + + override val defaultDisplay: StatusBarContentInsetsProvider + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): StatusBarContentInsetsProvider { + return perDisplayMockProviders.computeIfAbsent(displayId) { mock() } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStoreKosmos.kt new file mode 100644 index 000000000000..03b63c2a8757 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStoreKosmos.kt @@ -0,0 +1,26 @@ +/* + * 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.statusbar.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeStatusBarConfigurationControllerStore by Kosmos.Fixture { + FakeStatusBarConfigurationControllerStore() +} + +var Kosmos.statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore by + Kosmos.Fixture { fakeStatusBarConfigurationControllerStore } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt new file mode 100644 index 000000000000..a34fb0998c79 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt @@ -0,0 +1,44 @@ +/* + * 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.statusbar.data.repository + +import com.android.systemui.cameraProtectionLoaderFactory +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.display.data.repository.displayWindowPropertiesRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.phone.statusBarContentInsetsProviderFactory +import com.android.systemui.sysUICutoutProviderFactory + +val Kosmos.fakeStatusBarContentInsetsProviderStore by + Kosmos.Fixture { FakeStatusBarContentInsetsProviderStore() } + +val Kosmos.multiDisplayStatusBarContentInsetsProviderStore by + Kosmos.Fixture { + MultiDisplayStatusBarContentInsetsProviderStore( + applicationCoroutineScope, + displayRepository, + statusBarContentInsetsProviderFactory, + displayWindowPropertiesRepository, + statusBarConfigurationControllerStore, + sysUICutoutProviderFactory, + cameraProtectionLoaderFactory, + ) + } + +var Kosmos.statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore by + Kosmos.Fixture { fakeStatusBarContentInsetsProviderStore } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 7f4c670b05aa..31d390862540 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -23,6 +23,7 @@ import android.app.NotificationManager import android.content.Context import android.content.pm.LauncherApps import android.os.UserHandle +import android.os.UserManager import android.provider.DeviceConfig import androidx.core.os.bundleOf import com.android.internal.config.sysui.SystemUiDeviceConfigFlags @@ -85,7 +86,6 @@ import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent import com.android.systemui.util.Assert.runWithCurrentThreadAsMainThread import com.android.systemui.util.DeviceConfigProxyFake import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.wmshell.BubblesManager @@ -110,6 +110,7 @@ class ExpandableNotificationRowBuilder( private val mKeyguardBypassController: KeyguardBypassController private val mGroupMembershipManager: GroupMembershipManager private val mGroupExpansionManager: GroupExpansionManager + private val mUserManager: UserManager private val mHeadsUpManager: HeadsUpManager private val mIconManager: IconManager private val mContentBinder: NotificationRowContentBinder @@ -126,6 +127,7 @@ class ExpandableNotificationRowBuilder( private val mMainCoroutineContext = mTestScope.coroutineContext private val mFakeSystemClock = FakeSystemClock() private val mMainExecutor = FakeExecutor(mFakeSystemClock) + private val mDumpManager = DumpManager() init { featureFlags.setDefault(Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE) @@ -142,8 +144,8 @@ class ExpandableNotificationRowBuilder( mGroupMembershipManager = GroupMembershipManagerImpl() mSmartReplyController = Mockito.mock(SmartReplyController::class.java, STUB_ONLY) - val dumpManager = DumpManager() - mGroupExpansionManager = GroupExpansionManagerImpl(dumpManager, mGroupMembershipManager) + mGroupExpansionManager = GroupExpansionManagerImpl(mDumpManager, mGroupMembershipManager) + mUserManager = Mockito.mock(UserManager::class.java, STUB_ONLY) mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java, STUB_ONLY) mIconManager = IconManager( @@ -289,8 +291,8 @@ class ExpandableNotificationRowBuilder( NotificationOptimizedLinearLayoutFactory(), { Mockito.mock(NotificationViewFlipperFactory::class.java) }, NotificationRowIconViewInflaterFactory( - AppIconProviderImpl(context), - NotificationIconStyleProviderImpl(), + AppIconProviderImpl(context, mDumpManager), + NotificationIconStyleProviderImpl(mUserManager, mDumpManager), ), ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt index 08c6bbab6dd6..0fd0f1469818 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.notification.row.icon import android.content.applicationContext +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos -val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) } +val Kosmos.appIconProvider by + Kosmos.Fixture { AppIconProviderImpl(applicationContext, dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt index 611c90a6f4e8..b4fb7dd9d760 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.row.icon +import android.os.userManager +import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos -val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() } +val Kosmos.notificationIconStyleProvider by + Kosmos.Fixture { NotificationIconStyleProviderImpl(userManager, dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt index 3234e66024a8..83fc3e9f0c58 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -14,26 +14,29 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.applicationContext -import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.largeScreenHeaderHelper import com.android.systemui.statusbar.policy.splitShadeStateController +import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.sharedNotificationContainerInteractor by Kosmos.Fixture { SharedNotificationContainerInteractor( - configurationRepository = configurationRepository, context = applicationContext, splitShadeStateController = { splitShadeStateController }, shadeInteractor = { shadeInteractor }, + configurationInteractor = configurationInteractor, keyguardInteractor = keyguardInteractor, deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, - largeScreenHeaderHelperLazy = { largeScreenHeaderHelper } + largeScreenHeaderHelperLazy = { largeScreenHeaderHelper }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.kt new file mode 100644 index 000000000000..4fb8cf4a328b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.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.statusbar.phone + +import android.content.Context +import com.android.systemui.SysUICutoutProvider +import com.android.systemui.statusbar.policy.ConfigurationController +import org.mockito.kotlin.mock + +class FakeStatusBarContentInsetsProviderFactory : StatusBarContentInsetsProviderImpl.Factory { + + override fun create( + context: Context, + configurationController: ConfigurationController, + sysUICutoutProvider: SysUICutoutProvider, + ): StatusBarContentInsetsProviderImpl { + return mock<StatusBarContentInsetsProviderImpl>() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt index 9c9673c3a924..705df3c15d18 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt @@ -23,3 +23,9 @@ val Kosmos.mockStatusBarContentInsetsProvider by Kosmos.Fixture { mock<StatusBarContentInsetsProvider>() } var Kosmos.statusBarContentInsetsProvider by Kosmos.Fixture { mockStatusBarContentInsetsProvider } + +val Kosmos.fakeStatusBarContentInsetsProviderFactory by + Kosmos.Fixture { FakeStatusBarContentInsetsProviderFactory() } + +var Kosmos.statusBarContentInsetsProviderFactory: StatusBarContentInsetsProviderImpl.Factory by + Kosmos.Fixture { fakeStatusBarContentInsetsProviderFactory } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt index 6be13be407d8..32191277c94a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -23,7 +23,7 @@ class FakeConfigurationController @Inject constructor() : listeners -= listener } - override fun onConfigurationChanged(newConfiguration: Configuration?) { + override fun onConfigurationChanged(newConfiguration: Configuration) { listeners.forEach { it.onConfigChanged(newConfiguration) } } @@ -36,7 +36,7 @@ class FakeConfigurationController @Inject constructor() : } fun notifyConfigurationChanged() { - onConfigurationChanged(newConfiguration = null) + onConfigurationChanged(newConfiguration = Configuration()) } fun notifyLayoutDirectionChanged(isRtl: Boolean) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt index 65247a55348d..7eaecb1c4544 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt @@ -19,11 +19,13 @@ package com.android.systemui.statusbar.window import android.content.Context import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory { override fun create( context: Context, viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, statusBarConfigurationController: StatusBarConfigurationController, + contentInsetsProvider: StatusBarContentInsetsProvider, ) = FakeStatusBarWindowController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.kt new file mode 100644 index 000000000000..6e2d72256edc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/FieldSetter.kt @@ -0,0 +1,44 @@ +/* + * 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.utils + +import java.lang.reflect.Field + +object FieldSetter { + @JvmStatic + fun setField(obj: Any, fieldName: String, value: Any?) { + try { + val field = obj.javaClass.getDeclaredField(fieldName) + field.isAccessible = true + field[obj] = value + } catch (e: NoSuchFieldException) { + throw RuntimeException("Failed to set $fieldName of obj", e) + } catch (e: IllegalAccessException) { + throw RuntimeException("Failed to set $fieldName of obj", e) + } + } + + @JvmStatic + fun setField(obj: Any?, fld: Field, value: Any?) { + try { + fld.setAccessible(true) + fld.set(obj, value) + } catch (e: IllegalAccessException) { + throw RuntimeException("Failed to set ${fld.getName()} of obj", e) + } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt index 954084b874a0..c2a1544820c5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt @@ -14,17 +14,18 @@ * limitations under the License. */ -package com.android.systemui.qs.panels.domain.interactor +package com.android.systemui.volume.dialog.ringer.domain import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope -import com.android.systemui.log.core.FakeLogBuffer +import com.android.systemui.plugins.volumeDialogController +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor -val Kosmos.iconLabelVisibilityInteractor by +val Kosmos.volumeDialogRingerInteractor by Kosmos.Fixture { - IconLabelVisibilityInteractor( - qsPreferencesInteractor, - FakeLogBuffer.Factory.create(), - applicationCoroutineScope + VolumeDialogRingerInteractor( + coroutineScope = applicationCoroutineScope, + volumeDialogStateInteractor = volumeDialogStateInteractor, + controller = volumeDialogController, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt new file mode 100644 index 000000000000..db1c01a8698c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt @@ -0,0 +1,35 @@ +/* + * 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.volume.dialog.ringer.ui.viewmodel + +import com.android.systemui.haptics.vibratorHelper +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor +import com.android.systemui.volume.dialog.shared.volumeDialogLogger + +val Kosmos.volumeDialogRingerDrawerViewModel by + Kosmos.Fixture { + VolumeDialogRingerDrawerViewModel( + backgroundDispatcher = testDispatcher, + coroutineScope = applicationCoroutineScope, + interactor = volumeDialogRingerInteractor, + vibrator = vibratorHelper, + volumeDialogLogger = volumeDialogLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/shared/VolumeDialogLoggerKosmos.kt index 2c518863cf3c..f9d4a992cd63 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/VolumeDialogRingerRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/shared/VolumeDialogLoggerKosmos.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.systemui.volume.dialog.ringer.data +package com.android.systemui.volume.dialog.shared import com.android.systemui.kosmos.Kosmos +import com.android.systemui.log.logcatLogBuffer -val Kosmos.volumeDialogRingerRepository by Kosmos.Fixture { VolumeDialogRingerRepository() } +val Kosmos.volumeDialogLogger by Kosmos.Fixture { VolumeDialogLogger(logcatLogBuffer()) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt new file mode 100644 index 000000000000..423100a1addf --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * 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.volume.dialog.sliders.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.plugins.volumeDialogController +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor +import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType + +val Kosmos.volumeDialogSliderInteractor: VolumeDialogSliderInteractor by + Kosmos.Fixture { + VolumeDialogSliderInteractor( + volumeDialogSliderType, + volumeDialogStateInteractor, + volumeDialogController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt new file mode 100644 index 000000000000..cc8c1ea3fc75 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt @@ -0,0 +1,23 @@ +/* + * 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.volume.dialog.sliders.domain.model + +import android.media.AudioManager +import com.android.systemui.kosmos.Kosmos + +var Kosmos.volumeDialogSliderType: VolumeDialogSliderType by + Kosmos.Fixture { VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM) } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index ff2abd275f83..9a5e623b6d45 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -310,7 +310,7 @@ sh_test_host { name: "ravenwood-stats-checker", src: "scripts/ravenwood-stats-checker.sh", test_suites: ["general-tests"], - data: [ + device_common_data: [ ":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_stats.csv}", ":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_apis.csv}", ":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_keep_all.txt}", @@ -319,6 +319,16 @@ sh_test_host { ":services.core.ravenwood-base{hoststubgen_services.core_apis.csv}", ":services.core.ravenwood-base{hoststubgen_services.core_keep_all.txt}", ":services.core.ravenwood-base{hoststubgen_services.core_dump.txt}", + + ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_stats.csv}", + ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_apis.csv}", + ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_keep_all.txt}", + ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_dump.txt}", + + ":framework-statsd.ravenwood-base{framework-statsd_stats.csv}", + ":framework-statsd.ravenwood-base{framework-statsd_apis.csv}", + ":framework-statsd.ravenwood-base{framework-statsd_keep_all.txt}", + ":framework-statsd.ravenwood-base{framework-statsd_dump.txt}", ], } @@ -403,6 +413,9 @@ android_ravenwood_libgroup { // DeviceConfig "framework-configinfrastructure.ravenwood", + // StatsD + "framework-statsd.ravenwood", + // Provide runtime versions of utils linked in below "junit", "truth", diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp index 1bea434f5b49..d20773844df3 100644 --- a/ravenwood/Framework.bp +++ b/ravenwood/Framework.bp @@ -344,3 +344,57 @@ java_genrule { "framework-configinfrastructure.ravenwood.jar", ], } + +/////////////////////////////////// +// framework-statsd +/////////////////////////////////// + +java_genrule { + name: "framework-statsd.ravenwood-base", + tools: ["hoststubgen"], + cmd: "$(location hoststubgen) " + + "@$(location :ravenwood-standard-options) " + + + "--debug-log $(location framework-statsd.log) " + + "--stats-file $(location framework-statsd_stats.csv) " + + "--supported-api-list-file $(location framework-statsd_apis.csv) " + + "--gen-keep-all-file $(location framework-statsd_keep_all.txt) " + + "--gen-input-dump-file $(location framework-statsd_dump.txt) " + + + "--out-impl-jar $(location ravenwood.jar) " + + "--in-jar $(location :framework-statsd.impl{.jar}) " + + + "--policy-override-file $(location :ravenwood-common-policies) " + + "--policy-override-file $(location :framework-statsd-ravenwood-policies) ", + srcs: [ + ":framework-statsd.impl{.jar}", + + ":ravenwood-common-policies", + ":framework-statsd-ravenwood-policies", + ":ravenwood-standard-options", + ], + out: [ + "ravenwood.jar", + + // Following files are created just as FYI. + "framework-statsd_keep_all.txt", + "framework-statsd_dump.txt", + + "framework-statsd.log", + "framework-statsd_stats.csv", + "framework-statsd_apis.csv", + ], + visibility: ["//visibility:private"], +} + +java_genrule { + name: "framework-statsd.ravenwood", + defaults: ["ravenwood-internal-only-visibility-genrule"], + cmd: "cp $(in) $(out)", + srcs: [ + ":framework-statsd.ravenwood-base{ravenwood.jar}", + ], + out: [ + "framework-statsd.ravenwood.jar", + ], +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java index 478bead1354f..e0f9ec94a819 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java @@ -25,6 +25,7 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.AssumptionViolatedException; import org.junit.runner.Description; import org.junit.runners.model.TestClass; @@ -134,8 +135,17 @@ public class RavenwoodAwareTestRunnerHook { if (scope == Scope.Instance && order == Order.Outer) { // End of a test method. runner.mState.exitTestMethod(); - RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, - th == null ? Result.Passed : Result.Failed); + + final Result result; + if (th == null) { + result = Result.Passed; + } else if (th instanceof AssumptionViolatedException) { + result = Result.Skipped; + } else { + result = Result.Failed; + } + + RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result); } // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error. diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java deleted file mode 100644 index 1e3b3fcd733f..000000000000 --- a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java +++ /dev/null @@ -1,1035 +0,0 @@ -/* - * Copyright (C) 2019 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.util; - -// [ravenwood] This is an exact copy from StatsD, until we make StatsD available on Ravenwood. - -import static java.nio.charset.StandardCharsets.UTF_8; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.os.Build; -import android.os.SystemClock; - -import androidx.annotation.RequiresApi; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; - -import java.nio.ByteBuffer; -import java.util.Arrays; - -/** - * StatsEvent builds and stores the buffer sent over the statsd socket. - * This class defines and encapsulates the socket protocol. - * - * <p>Usage:</p> - * <pre> - * // Pushed event - * StatsEvent statsEvent = StatsEvent.newBuilder() - * .setAtomId(atomId) - * .writeBoolean(false) - * .writeString("annotated String field") - * .addBooleanAnnotation(annotationId, true) - * .usePooledBuffer() - * .build(); - * StatsLog.write(statsEvent); - * - * // Pulled event - * StatsEvent statsEvent = StatsEvent.newBuilder() - * .setAtomId(atomId) - * .writeBoolean(false) - * .writeString("annotated String field") - * .addBooleanAnnotation(annotationId, true) - * .build(); - * </pre> - * @hide - **/ -@SystemApi -public final class StatsEvent { - // Type Ids. - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_INT = 0x00; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_LONG = 0x01; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_STRING = 0x02; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_LIST = 0x03; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_FLOAT = 0x04; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_BOOLEAN = 0x05; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_BYTE_ARRAY = 0x06; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_OBJECT = 0x07; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_KEY_VALUE_PAIRS = 0x08; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_ATTRIBUTION_CHAIN = 0x09; - - /** - * @hide - **/ - @VisibleForTesting - public static final byte TYPE_ERRORS = 0x0F; - - // Error flags. - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_NO_TIMESTAMP = 0x1; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_NO_ATOM_ID = 0x2; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_OVERFLOW = 0x4; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_TOO_MANY_KEY_VALUE_PAIRS = 0x10; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x20; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_INVALID_ANNOTATION_ID = 0x40; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x80; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_TOO_MANY_ANNOTATIONS = 0x100; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_TOO_MANY_FIELDS = 0x200; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000; - - /** - * @hide - **/ - @VisibleForTesting - public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000; - - /** - * @hide - **/ - @VisibleForTesting public static final int ERROR_LIST_TOO_LONG = 0x4000; - - // Size limits. - - /** - * @hide - **/ - @VisibleForTesting - public static final int MAX_ANNOTATION_COUNT = 15; - - /** - * @hide - **/ - @VisibleForTesting - public static final int MAX_ATTRIBUTION_NODES = 127; - - /** - * @hide - **/ - @VisibleForTesting - public static final int MAX_NUM_ELEMENTS = 127; - - /** - * @hide - **/ - @VisibleForTesting - public static final int MAX_KEY_VALUE_PAIRS = 127; - - private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068; - - // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag. - // See android_util_StatsLog.cpp. - private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4; - - private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB - - private final int mAtomId; - private final byte[] mPayload; - private Buffer mBuffer; - private final int mNumBytes; - - private StatsEvent(final int atomId, @Nullable final Buffer buffer, - @NonNull final byte[] payload, final int numBytes) { - mAtomId = atomId; - mBuffer = buffer; - mPayload = payload; - mNumBytes = numBytes; - } - - /** - * Returns a new StatsEvent.Builder for building StatsEvent object. - **/ - @NonNull - public static Builder newBuilder() { - return new Builder(Buffer.obtain()); - } - - /** - * Get the atom Id of the atom encoded in this StatsEvent object. - * - * @hide - **/ - public int getAtomId() { - return mAtomId; - } - - /** - * Get the byte array that contains the encoded payload that can be sent to statsd. - * - * @hide - **/ - @NonNull - public byte[] getBytes() { - return mPayload; - } - - /** - * Get the number of bytes used to encode the StatsEvent payload. - * - * @hide - **/ - public int getNumBytes() { - return mNumBytes; - } - - /** - * Recycle resources used by this StatsEvent object. - * No actions should be taken on this StatsEvent after release() is called. - * - * @hide - **/ - public void release() { - if (mBuffer != null) { - mBuffer.release(); - mBuffer = null; - } - } - - /** - * Builder for constructing a StatsEvent object. - * - * <p>This class defines and encapsulates the socket encoding for the - *buffer. The write methods must be called in the same order as the order of - *fields in the atom definition.</p> - * - * <p>setAtomId() must be called immediately after - *StatsEvent.newBuilder().</p> - * - * <p>Example:</p> - * <pre> - * // Atom definition. - * message MyAtom { - * optional int32 field1 = 1; - * optional int64 field2 = 2; - * optional string field3 = 3 [(annotation1) = true]; - * optional repeated int32 field4 = 4; - * } - * - * // StatsEvent construction for pushed event. - * StatsEvent.newBuilder() - * StatsEvent statsEvent = StatsEvent.newBuilder() - * .setAtomId(atomId) - * .writeInt(3) // field1 - * .writeLong(8L) // field2 - * .writeString("foo") // field 3 - * .addBooleanAnnotation(annotation1Id, true) - * .writeIntArray({ 1, 2, 3 }); - * .usePooledBuffer() - * .build(); - * - * // StatsEvent construction for pulled event. - * StatsEvent.newBuilder() - * StatsEvent statsEvent = StatsEvent.newBuilder() - * .setAtomId(atomId) - * .writeInt(3) // field1 - * .writeLong(8L) // field2 - * .writeString("foo") // field 3 - * .addBooleanAnnotation(annotation1Id, true) - * .writeIntArray({ 1, 2, 3 }); - * .build(); - * </pre> - **/ - public static final class Builder { - // Fixed positions. - private static final int POS_NUM_ELEMENTS = 1; - private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES; - private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES; - - private final Buffer mBuffer; - private long mTimestampNs; - private int mAtomId; - private byte mCurrentAnnotationCount; - private int mPos; - private int mPosLastField; - private byte mLastType; - private int mNumElements; - private int mErrorMask; - private boolean mUsePooledBuffer = false; - - private Builder(final Buffer buffer) { - mBuffer = buffer; - mCurrentAnnotationCount = 0; - mAtomId = 0; - mTimestampNs = SystemClock.elapsedRealtimeNanos(); - mNumElements = 0; - - // Set mPos to 0 for writing TYPE_OBJECT at 0th position. - mPos = 0; - writeTypeId(TYPE_OBJECT); - - // Write timestamp. - mPos = POS_TIMESTAMP_NS; - writeLong(mTimestampNs); - } - - /** - * Sets the atom id for this StatsEvent. - * - * This should be called immediately after StatsEvent.newBuilder() - * and should only be called once. - * Not calling setAtomId will result in ERROR_NO_ATOM_ID. - * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION. - **/ - @NonNull - public Builder setAtomId(final int atomId) { - if (0 == mAtomId) { - mAtomId = atomId; - - if (1 == mNumElements) { // Only timestamp is written so far. - writeInt(atomId); - } else { - // setAtomId called out of order. - mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION; - } - } - - return this; - } - - /** - * Write a boolean field to this StatsEvent. - **/ - @NonNull - public Builder writeBoolean(final boolean value) { - // Write boolean typeId byte followed by boolean byte representation. - writeTypeId(TYPE_BOOLEAN); - mPos += mBuffer.putBoolean(mPos, value); - mNumElements++; - return this; - } - - /** - * Write an integer field to this StatsEvent. - **/ - @NonNull - public Builder writeInt(final int value) { - // Write integer typeId byte followed by 4-byte representation of value. - writeTypeId(TYPE_INT); - mPos += mBuffer.putInt(mPos, value); - mNumElements++; - return this; - } - - /** - * Write a long field to this StatsEvent. - **/ - @NonNull - public Builder writeLong(final long value) { - // Write long typeId byte followed by 8-byte representation of value. - writeTypeId(TYPE_LONG); - mPos += mBuffer.putLong(mPos, value); - mNumElements++; - return this; - } - - /** - * Write a float field to this StatsEvent. - **/ - @NonNull - public Builder writeFloat(final float value) { - // Write float typeId byte followed by 4-byte representation of value. - writeTypeId(TYPE_FLOAT); - mPos += mBuffer.putFloat(mPos, value); - mNumElements++; - return this; - } - - /** - * Write a String field to this StatsEvent. - **/ - @NonNull - public Builder writeString(@NonNull final String value) { - // Write String typeId byte, followed by 4-byte representation of number of bytes - // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value. - final byte[] valueBytes = stringToBytes(value); - writeByteArray(valueBytes, TYPE_STRING); - return this; - } - - /** - * Write a byte array field to this StatsEvent. - **/ - @NonNull - public Builder writeByteArray(@NonNull final byte[] value) { - // Write byte array typeId byte, followed by 4-byte representation of number of bytes - // in value, followed by the actual byte array. - writeByteArray(value, TYPE_BYTE_ARRAY); - return this; - } - - private void writeByteArray(@NonNull final byte[] value, final byte typeId) { - writeTypeId(typeId); - final int numBytes = value.length; - mPos += mBuffer.putInt(mPos, numBytes); - mPos += mBuffer.putByteArray(mPos, value); - mNumElements++; - } - - /** - * Write an attribution chain field to this StatsEvent. - * - * The sizes of uids and tags must be equal. The AttributionNode at position i is - * made up of uids[i] and tags[i]. - * - * @param uids array of uids in the attribution nodes. - * @param tags array of tags in the attribution nodes. - **/ - @NonNull - public Builder writeAttributionChain( - @NonNull final int[] uids, @NonNull final String[] tags) { - final byte numUids = (byte) uids.length; - final byte numTags = (byte) tags.length; - - if (numUids != numTags) { - mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL; - } else if (numUids > MAX_ATTRIBUTION_NODES) { - mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG; - } else { - // Write attribution chain typeId byte, followed by 1-byte representation of - // number of attribution nodes, followed by encoding of each attribution node. - writeTypeId(TYPE_ATTRIBUTION_CHAIN); - mPos += mBuffer.putByte(mPos, numUids); - for (int i = 0; i < numUids; i++) { - // Each uid is encoded as 4-byte representation of its int value. - mPos += mBuffer.putInt(mPos, uids[i]); - - // Each tag is encoded as 4-byte representation of number of bytes in its - // UTF-8 encoding, followed by the actual UTF-8 bytes. - final byte[] tagBytes = stringToBytes(tags[i]); - mPos += mBuffer.putInt(mPos, tagBytes.length); - mPos += mBuffer.putByteArray(mPos, tagBytes); - } - mNumElements++; - } - return this; - } - - /** - * Write KeyValuePairsAtom entries to this StatsEvent. - * - * @param intMap Integer key-value pairs. - * @param longMap Long key-value pairs. - * @param stringMap String key-value pairs. - * @param floatMap Float key-value pairs. - **/ - @NonNull - public Builder writeKeyValuePairs( - @Nullable final SparseIntArray intMap, - @Nullable final SparseLongArray longMap, - @Nullable final SparseArray<String> stringMap, - @Nullable final SparseArray<Float> floatMap) { - final int intMapSize = null == intMap ? 0 : intMap.size(); - final int longMapSize = null == longMap ? 0 : longMap.size(); - final int stringMapSize = null == stringMap ? 0 : stringMap.size(); - final int floatMapSize = null == floatMap ? 0 : floatMap.size(); - final int totalCount = intMapSize + longMapSize + stringMapSize + floatMapSize; - - if (totalCount > MAX_KEY_VALUE_PAIRS) { - mErrorMask |= ERROR_TOO_MANY_KEY_VALUE_PAIRS; - } else { - writeTypeId(TYPE_KEY_VALUE_PAIRS); - mPos += mBuffer.putByte(mPos, (byte) totalCount); - - for (int i = 0; i < intMapSize; i++) { - final int key = intMap.keyAt(i); - final int value = intMap.valueAt(i); - mPos += mBuffer.putInt(mPos, key); - writeTypeId(TYPE_INT); - mPos += mBuffer.putInt(mPos, value); - } - - for (int i = 0; i < longMapSize; i++) { - final int key = longMap.keyAt(i); - final long value = longMap.valueAt(i); - mPos += mBuffer.putInt(mPos, key); - writeTypeId(TYPE_LONG); - mPos += mBuffer.putLong(mPos, value); - } - - for (int i = 0; i < stringMapSize; i++) { - final int key = stringMap.keyAt(i); - final String value = stringMap.valueAt(i); - mPos += mBuffer.putInt(mPos, key); - writeTypeId(TYPE_STRING); - final byte[] valueBytes = stringToBytes(value); - mPos += mBuffer.putInt(mPos, valueBytes.length); - mPos += mBuffer.putByteArray(mPos, valueBytes); - } - - for (int i = 0; i < floatMapSize; i++) { - final int key = floatMap.keyAt(i); - final float value = floatMap.valueAt(i); - mPos += mBuffer.putInt(mPos, key); - writeTypeId(TYPE_FLOAT); - mPos += mBuffer.putFloat(mPos, value); - } - - mNumElements++; - } - - return this; - } - - /** - * Write a repeated boolean field to this StatsEvent. - * - * The list size must not exceed 127. Otherwise, the array isn't written - * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the - * StatsEvent errors field. - * - * @param elements array of booleans. - **/ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - @NonNull - public Builder writeBooleanArray(@NonNull final boolean[] elements) { - final byte numElements = (byte)elements.length; - - if (writeArrayInfo(numElements, TYPE_BOOLEAN)) { - // Write encoding of each element. - for (int i = 0; i < numElements; i++) { - mPos += mBuffer.putBoolean(mPos, elements[i]); - } - mNumElements++; - } - return this; - } - - /** - * Write a repeated int field to this StatsEvent. - * - * The list size must not exceed 127. Otherwise, the array isn't written - * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the - * StatsEvent errors field. - * - * @param elements array of ints. - **/ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - @NonNull - public Builder writeIntArray(@NonNull final int[] elements) { - final byte numElements = (byte)elements.length; - - if (writeArrayInfo(numElements, TYPE_INT)) { - // Write encoding of each element. - for (int i = 0; i < numElements; i++) { - mPos += mBuffer.putInt(mPos, elements[i]); - } - mNumElements++; - } - return this; - } - - /** - * Write a repeated long field to this StatsEvent. - * - * The list size must not exceed 127. Otherwise, the array isn't written - * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the - * StatsEvent errors field. - * - * @param elements array of longs. - **/ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - @NonNull - public Builder writeLongArray(@NonNull final long[] elements) { - final byte numElements = (byte)elements.length; - - if (writeArrayInfo(numElements, TYPE_LONG)) { - // Write encoding of each element. - for (int i = 0; i < numElements; i++) { - mPos += mBuffer.putLong(mPos, elements[i]); - } - mNumElements++; - } - return this; - } - - /** - * Write a repeated float field to this StatsEvent. - * - * The list size must not exceed 127. Otherwise, the array isn't written - * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the - * StatsEvent errors field. - * - * @param elements array of floats. - **/ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - @NonNull - public Builder writeFloatArray(@NonNull final float[] elements) { - final byte numElements = (byte)elements.length; - - if (writeArrayInfo(numElements, TYPE_FLOAT)) { - // Write encoding of each element. - for (int i = 0; i < numElements; i++) { - mPos += mBuffer.putFloat(mPos, elements[i]); - } - mNumElements++; - } - return this; - } - - /** - * Write a repeated string field to this StatsEvent. - * - * The list size must not exceed 127. Otherwise, the array isn't written - * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the - * StatsEvent errors field. - * - * @param elements array of strings. - **/ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - @NonNull - public Builder writeStringArray(@NonNull final String[] elements) { - final byte numElements = (byte)elements.length; - - if (writeArrayInfo(numElements, TYPE_STRING)) { - // Write encoding of each element. - for (int i = 0; i < numElements; i++) { - final byte[] elementBytes = stringToBytes(elements[i]); - mPos += mBuffer.putInt(mPos, elementBytes.length); - mPos += mBuffer.putByteArray(mPos, elementBytes); - } - mNumElements++; - } - return this; - } - - /** - * Write a boolean annotation for the last field written. - **/ - @NonNull - public Builder addBooleanAnnotation( - final byte annotationId, final boolean value) { - // Ensure there's a field written to annotate. - if (mNumElements < 2) { - mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; - } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { - mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; - } else { - mPos += mBuffer.putByte(mPos, annotationId); - mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN); - mPos += mBuffer.putBoolean(mPos, value); - mCurrentAnnotationCount++; - writeAnnotationCount(); - } - - return this; - } - - /** - * Write an integer annotation for the last field written. - **/ - @NonNull - public Builder addIntAnnotation(final byte annotationId, final int value) { - if (mNumElements < 2) { - mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD; - } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) { - mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS; - } else { - mPos += mBuffer.putByte(mPos, annotationId); - mPos += mBuffer.putByte(mPos, TYPE_INT); - mPos += mBuffer.putInt(mPos, value); - mCurrentAnnotationCount++; - writeAnnotationCount(); - } - - return this; - } - - /** - * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent. - * This should be called for pushed events to reduce memory allocations and garbage - * collections. - **/ - @NonNull - public Builder usePooledBuffer() { - mUsePooledBuffer = true; - mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos); - return this; - } - - /** - * Builds a StatsEvent object with values entered in this Builder. - **/ - @NonNull - public StatsEvent build() { - if (0L == mTimestampNs) { - mErrorMask |= ERROR_NO_TIMESTAMP; - } - if (0 == mAtomId) { - mErrorMask |= ERROR_NO_ATOM_ID; - } - if (mBuffer.hasOverflowed()) { - mErrorMask |= ERROR_OVERFLOW; - } - if (mNumElements > MAX_NUM_ELEMENTS) { - mErrorMask |= ERROR_TOO_MANY_FIELDS; - } - - if (0 == mErrorMask) { - mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements); - } else { - // Write atom id and error mask. Overwrite any annotations for atom Id. - mPos = POS_ATOM_ID; - mPos += mBuffer.putByte(mPos, TYPE_INT); - mPos += mBuffer.putInt(mPos, mAtomId); - mPos += mBuffer.putByte(mPos, TYPE_ERRORS); - mPos += mBuffer.putInt(mPos, mErrorMask); - mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3); - } - - final int size = mPos; - - if (mUsePooledBuffer) { - return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size); - } else { - // Create a copy of the buffer with the required number of bytes. - final byte[] payload = new byte[size]; - System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size); - - // Return Buffer instance to the pool. - mBuffer.release(); - - return new StatsEvent(mAtomId, null, payload, size); - } - } - - private void writeTypeId(final byte typeId) { - mPosLastField = mPos; - mLastType = typeId; - mCurrentAnnotationCount = 0; - final byte encodedId = (byte) (typeId & 0x0F); - mPos += mBuffer.putByte(mPos, encodedId); - } - - private void writeAnnotationCount() { - // Use first 4 bits for annotation count and last 4 bits for typeId. - final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F)); - mBuffer.putByte(mPosLastField, encodedId); - } - - @NonNull - private static byte[] stringToBytes(@Nullable final String value) { - return (null == value ? "" : value).getBytes(UTF_8); - } - - private boolean writeArrayInfo(final byte numElements, - final byte elementTypeId) { - if (numElements > MAX_NUM_ELEMENTS) { - mErrorMask |= ERROR_LIST_TOO_LONG; - return false; - } - // Write list typeId byte, 1-byte representation of number of - // elements, and element typeId byte. - writeTypeId(TYPE_LIST); - mPos += mBuffer.putByte(mPos, numElements); - // Write element typeId byte without setting mPosLastField and mLastType (i.e. don't use - // #writeTypeId) - final byte encodedId = (byte) (elementTypeId & 0x0F); - mPos += mBuffer.putByte(mPos, encodedId); - return true; - } - } - - private static final class Buffer { - private static Object sLock = new Object(); - - @GuardedBy("sLock") - private static Buffer sPool; - - private byte[] mBytes; - private boolean mOverflow = false; - private int mMaxSize = MAX_PULL_PAYLOAD_SIZE; - - @NonNull - private static Buffer obtain() { - final Buffer buffer; - synchronized (sLock) { - buffer = null == sPool ? new Buffer() : sPool; - sPool = null; - } - buffer.reset(); - return buffer; - } - - private Buffer() { - final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(MAX_PUSH_PAYLOAD_SIZE); - mBytes = tempBuffer.hasArray() ? tempBuffer.array() : new byte [MAX_PUSH_PAYLOAD_SIZE]; - } - - @NonNull - private byte[] getBytes() { - return mBytes; - } - - private void release() { - // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under. - if (mMaxSize <= MAX_PUSH_PAYLOAD_SIZE) { - synchronized (sLock) { - if (null == sPool) { - sPool = this; - } - } - } - } - - private void reset() { - mOverflow = false; - mMaxSize = MAX_PULL_PAYLOAD_SIZE; - } - - private void setMaxSize(final int maxSize, final int numBytesWritten) { - mMaxSize = maxSize; - if (numBytesWritten > maxSize) { - mOverflow = true; - } - } - - private boolean hasOverflowed() { - return mOverflow; - } - - /** - * Checks for available space in the byte array. - * - * @param index starting position in the buffer to start the check. - * @param numBytes number of bytes to check from index. - * @return true if space is available, false otherwise. - **/ - private boolean hasEnoughSpace(final int index, final int numBytes) { - final int totalBytesNeeded = index + numBytes; - - if (totalBytesNeeded > mMaxSize) { - mOverflow = true; - return false; - } - - // Expand buffer if needed. - if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) { - int newSize = mBytes.length; - do { - newSize *= 2; - } while (newSize <= totalBytesNeeded); - - if (newSize > mMaxSize) { - newSize = mMaxSize; - } - - mBytes = Arrays.copyOf(mBytes, newSize); - } - - return true; - } - - /** - * Writes a byte into the buffer. - * - * @param index position in the buffer where the byte is written. - * @param value the byte to write. - * @return number of bytes written to buffer from this write operation. - **/ - private int putByte(final int index, final byte value) { - if (hasEnoughSpace(index, Byte.BYTES)) { - mBytes[index] = (byte) (value); - return Byte.BYTES; - } - return 0; - } - - /** - * Writes a boolean into the buffer. - * - * @param index position in the buffer where the boolean is written. - * @param value the boolean to write. - * @return number of bytes written to buffer from this write operation. - **/ - private int putBoolean(final int index, final boolean value) { - return putByte(index, (byte) (value ? 1 : 0)); - } - - /** - * Writes an integer into the buffer. - * - * @param index position in the buffer where the integer is written. - * @param value the integer to write. - * @return number of bytes written to buffer from this write operation. - **/ - private int putInt(final int index, final int value) { - if (hasEnoughSpace(index, Integer.BYTES)) { - // Use little endian byte order. - mBytes[index] = (byte) (value); - mBytes[index + 1] = (byte) (value >> 8); - mBytes[index + 2] = (byte) (value >> 16); - mBytes[index + 3] = (byte) (value >> 24); - return Integer.BYTES; - } - return 0; - } - - /** - * Writes a long into the buffer. - * - * @param index position in the buffer where the long is written. - * @param value the long to write. - * @return number of bytes written to buffer from this write operation. - **/ - private int putLong(final int index, final long value) { - if (hasEnoughSpace(index, Long.BYTES)) { - // Use little endian byte order. - mBytes[index] = (byte) (value); - mBytes[index + 1] = (byte) (value >> 8); - mBytes[index + 2] = (byte) (value >> 16); - mBytes[index + 3] = (byte) (value >> 24); - mBytes[index + 4] = (byte) (value >> 32); - mBytes[index + 5] = (byte) (value >> 40); - mBytes[index + 6] = (byte) (value >> 48); - mBytes[index + 7] = (byte) (value >> 56); - return Long.BYTES; - } - return 0; - } - - /** - * Writes a float into the buffer. - * - * @param index position in the buffer where the float is written. - * @param value the float to write. - * @return number of bytes written to buffer from this write operation. - **/ - private int putFloat(final int index, final float value) { - return putInt(index, Float.floatToIntBits(value)); - } - - /** - * Copies a byte array into the buffer. - * - * @param index position in the buffer where the byte array is copied. - * @param value the byte array to copy. - * @return number of bytes written to buffer from this write operation. - **/ - private int putByteArray(final int index, @NonNull final byte[] value) { - final int numBytes = value.length; - if (hasEnoughSpace(index, numBytes)) { - System.arraycopy(value, 0, mBytes, index, numBytes); - return numBytes; - } - return 0; - } - } -} diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java deleted file mode 100644 index c1c20cfac9dd..000000000000 --- a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (C) 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.util; - -/* - * [Ravenwood] This is copied from StatsD, with the following changes: - * - The static {} is commented out. - * - All references to IStatsD and StatsdStatsLog are commented out. - * - The native method is no-oped. - */ - -import static android.Manifest.permission.DUMP; -import static android.Manifest.permission.PACKAGE_USAGE_STATS; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; -import android.annotation.SystemApi; -import android.os.Build; -//import android.os.IStatsd; -import android.os.Process; -import android.util.proto.ProtoOutputStream; - -import androidx.annotation.RequiresApi; - -//import com.android.internal.statsd.StatsdStatsLog; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * StatsLog provides an API for developers to send events to statsd. The events can be used to - * define custom metrics in side statsd. - */ -public final class StatsLog { - -// // Load JNI library -// static { -// System.loadLibrary("stats_jni"); -// } - private static final String TAG = "StatsLog"; - private static final boolean DEBUG = false; - private static final int EXPERIMENT_IDS_FIELD_ID = 1; - - /** - * Annotation ID constant for logging UID field. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_IS_UID = 1; - - /** - * Annotation ID constant to indicate logged atom event's timestamp should be truncated. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; - - /** - * Annotation ID constant for a state atom's primary field. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3; - - /** - * Annotation ID constant for state atom's state field. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4; - - /** - * Annotation ID constant to indicate the first UID in the attribution chain - * is a primary field. - * Should only be used for attribution chain fields. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; - - /** - * Annotation ID constant to indicate which state is default for the state atom. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_DEFAULT_STATE = 6; - - /** - * Annotation ID constant to signal all states should be reset to the default state. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7; - - /** - * Annotation ID constant to indicate state changes need to account for nesting. - * This should only be used with binary state atoms. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - public static final byte ANNOTATION_ID_STATE_NESTED = 8; - - /** - * Annotation ID constant to indicate the restriction category of an atom. - * This annotation must only be attached to the atom id. This is an int annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9; - - /** - * Annotation ID to indicate that a field of an atom contains peripheral device info. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10; - - /** - * Annotation ID to indicate that a field of an atom contains app usage information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11; - - /** - * Annotation ID to indicate that a field of an atom contains app activity information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12; - - /** - * Annotation ID to indicate that a field of an atom contains health connect information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13; - - /** - * Annotation ID to indicate that a field of an atom contains accessibility information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14; - - /** - * Annotation ID to indicate that a field of an atom contains system search information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15; - - /** - * Annotation ID to indicate that a field of an atom contains user engagement information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16; - - /** - * Annotation ID to indicate that a field of an atom contains ambient sensing information. - * This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17; - - /** - * Annotation ID to indicate that a field of an atom contains demographic classification - * information. This is a bool annotation. - * - * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() - * accept byte as the type for annotation ids to save space. - * - * @hide - */ - @SuppressLint("NoByteOrShort") - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18; - - - /** @hide */ - @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = { - RESTRICTION_CATEGORY_DIAGNOSTIC, - RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE, - RESTRICTION_CATEGORY_AUTHENTICATION, - RESTRICTION_CATEGORY_FRAUD_AND_ABUSE}) - @Retention(RetentionPolicy.SOURCE) - public @interface RestrictionCategory {} - - /** - * Restriction category for atoms about diagnostics. - * - * @hide - */ - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1; - - /** - * Restriction category for atoms about system intelligence. - * - * @hide - */ - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2; - - /** - * Restriction category for atoms about authentication. - * - * @hide - */ - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3; - - /** - * Restriction category for atoms about fraud and abuse. - * - * @hide - */ - @SystemApi - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4; - - private StatsLog() { - } - - /** - * Logs a start event. - * - * @param label developer-chosen label. - * @return True if the log request was sent to statsd. - */ - public static boolean logStart(int label) { - int callingUid = Process.myUid(); -// StatsdStatsLog.write( -// StatsdStatsLog.APP_BREADCRUMB_REPORTED, -// callingUid, -// label, -// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); - return true; - } - - /** - * Logs a stop event. - * - * @param label developer-chosen label. - * @return True if the log request was sent to statsd. - */ - public static boolean logStop(int label) { - int callingUid = Process.myUid(); -// StatsdStatsLog.write( -// StatsdStatsLog.APP_BREADCRUMB_REPORTED, -// callingUid, -// label, -// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); - return true; - } - - /** - * Logs an event that does not represent a start or stop boundary. - * - * @param label developer-chosen label. - * @return True if the log request was sent to statsd. - */ - public static boolean logEvent(int label) { - int callingUid = Process.myUid(); -// StatsdStatsLog.write( -// StatsdStatsLog.APP_BREADCRUMB_REPORTED, -// callingUid, -// label, -// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); - return true; - } - - /** - * Logs an event for binary push for module updates. - * - * @param trainName name of install train. - * @param trainVersionCode version code of the train. - * @param options optional flags about this install. - * The last 3 bits indicate options: - * 0x01: FLAG_REQUIRE_STAGING - * 0x02: FLAG_ROLLBACK_ENABLED - * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR - * @param state current install state. Defined as State enums in - * BinaryPushStateChanged atom in - * frameworks/proto_logging/stats/atoms.proto - * @param experimentIds experiment ids. - * @return True if the log request was sent to statsd. - */ - @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) - public static boolean logBinaryPushStateChanged(@NonNull String trainName, - long trainVersionCode, int options, int state, - @NonNull long[] experimentIds) { - ProtoOutputStream proto = new ProtoOutputStream(); - for (long id : experimentIds) { - proto.write( - ProtoOutputStream.FIELD_TYPE_INT64 - | ProtoOutputStream.FIELD_COUNT_REPEATED - | EXPERIMENT_IDS_FIELD_ID, - id); - } -// StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED, -// trainName, -// trainVersionCode, -// (options & IStatsd.FLAG_REQUIRE_STAGING) > 0, -// (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0, -// (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0, -// state, -// proto.getBytes(), -// 0, -// 0, -// false); - return true; - } - - /** - * Write an event to stats log using the raw format. - * - * @param buffer The encoded buffer of data to write. - * @param size The number of bytes from the buffer to write. - * @hide - * @deprecated Use {@link write(final StatsEvent statsEvent)} instead. - * - */ - @Deprecated - @SystemApi - public static void writeRaw(@NonNull byte[] buffer, int size) { - writeImpl(buffer, size, 0); - } - - /** - * Write an event to stats log using the raw format. - * - * @param buffer The encoded buffer of data to write. - * @param size The number of bytes from the buffer to write. - * @param atomId The id of the atom to which the event belongs. - */ -// private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId); - private static void writeImpl(@NonNull byte[] buffer, int size, int atomId) { - // no-op for now - } - - /** - * Write an event to stats log using the raw format encapsulated in StatsEvent. - * After writing to stats log, release() is called on the StatsEvent object. - * No further action should be taken on the StatsEvent object following this call. - * - * @param statsEvent The StatsEvent object containing the encoded buffer of data to write. - * @hide - */ - @SystemApi - public static void write(@NonNull final StatsEvent statsEvent) { - writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId()); - statsEvent.release(); - } -} diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh index 36601bde87cd..b83216af95fe 100755 --- a/ravenwood/scripts/ravenwood-stats-collector.sh +++ b/ravenwood/scripts/ravenwood-stats-collector.sh @@ -62,6 +62,8 @@ collect_stats() { dump "framework-minus-apex" hoststubgen_framework-minus-apex_stats.csv dump "service.core" hoststubgen_services.core_stats.csv + dump "framework-configinfrastructure" framework-configinfrastructure_stats.csv + dump "framework-statsd" framework-statsd_stats.csv } > "$out" echo "Stats CVS created at $out" @@ -76,6 +78,8 @@ collect_apis() { dump "framework-minus-apex" hoststubgen_framework-minus-apex_apis.csv dump "service.core" hoststubgen_services.core_apis.csv + dump "framework-configinfrastructure" framework-configinfrastructure_apis.csv + dump "framework-statsd" framework-statsd_apis.csv } > "$out" echo "API CVS created at $out" diff --git a/ravenwood/scripts/ravenwood-test-summary b/ravenwood/scripts/ravenwood-test-summary new file mode 100755 index 000000000000..602fbff31ea3 --- /dev/null +++ b/ravenwood/scripts/ravenwood-test-summary @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# 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. + +''' +Print the latest Ravenwood test execution summary + +Usage: /ravenwood-test-summary + +Example output: +Module Passed Failed Skipped +android.test.mock.ravenwood.tests 2 0 0 +CarLibHostUnitTest 565 0 7 +CarServiceHostUnitTest 364 0 0 +CtsAccountManagerTestCasesRavenwood 4 0 0 +CtsAppTestCasesRavenwood 21 0 0 + +Description: +This script finds all the test execution result from /tmp/Ravenwood-stats*, +and shows per-module summary. +''' + +import csv +import glob +import sys + +# Find the latest stats files. +stats_files = glob.glob('/tmp/Ravenwood-stats_*_latest.csv') + +if len(stats_files) == 0: + print("No log files found.", file=sys.stderr) + exit(1) + + +def parse_stats(file, result): + module = '(unknwon)' + passed = 0 + failed = 0 + skipped = 0 + with open(file) as csvfile: + reader = csv.reader(csvfile, delimiter=',') + + + for i, row in enumerate(reader): + if i == 0: continue # Skip header line + module = row[0] + passed += int(row[3]) + failed += int(row[4]) + skipped += int(row[5]) + + result[module] = (passed, failed, skipped) + + +result = {} + +for file in stats_files: + parse_stats(file, result) + +print('%-60s %8s %8s %8s' % ("Module", "Passed", "Failed", "Skipped")) + +for module in sorted(result.keys(), key=str.casefold): + r = result[module] + print('%-60s %8d %8d %8d' % (module, r[0], r[1], r[2])) diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh index 926c08f4e689..5d623e0b6c36 100755 --- a/ravenwood/scripts/run-ravenwood-tests.sh +++ b/ravenwood/scripts/run-ravenwood-tests.sh @@ -14,15 +14,42 @@ # limitations under the License. # Run all the ravenwood tests + hoststubgen unit tests. +# +# Options: +# +# -s: "Smoke" test -- skip slow tests (SysUI, ICU) + +smoke=0 +while getopts "s" opt; do +case "$opt" in + s) + smoke=1 + ;; + '?') + exit 1 + ;; +esac +done +shift $(($OPTIND - 1)) + +all_tests=(hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker) +all_tests+=( $(${0%/*}/list-ravenwood-tests.sh) ) -all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker" +# Regex to identify slow tests, in PCRE +slow_tests_re='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$' -# "echo" is to remove the newlines -all_tests="$all_tests $(echo $(${0%/*}/list-ravenwood-tests.sh) )" +if (( $smoke )) ; then + # Remove the slow tests. + all_tests=( $( + for t in "${all_tests[@]}"; do + echo $t | grep -vP "$slow_tests_re" + done + ) ) +fi run() { echo "Running: $*" "${@}" } -run ${ATEST:-atest} $all_tests +run ${ATEST:-atest} "${all_tests[@]}" diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp index 85f1baf1cab9..412744eb9d34 100644 --- a/ravenwood/tests/coretest/Android.bp +++ b/ravenwood/tests/coretest/Android.bp @@ -23,6 +23,7 @@ android_ravenwood_test { ], srcs: [ "test/**/*.java", + "test/**/*.kt", ], ravenizer: { strip_mockito: true, diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt new file mode 100644 index 000000000000..d5f5e297b396 --- /dev/null +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt @@ -0,0 +1,26 @@ +/* + * 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.ravenwoodtest.coretest + +import com.android.internal.util.FrameworkStatsLog +import org.junit.Test + +class RavenwoodStatsDTest { + @Test + fun testFrameworkStatsLog() { + FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, 123) + } +}
\ No newline at end of file diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt index b64944ee39ed..80126df1b8df 100644 --- a/ravenwood/texts/ravenwood-framework-policies.txt +++ b/ravenwood/texts/ravenwood-framework-policies.txt @@ -5,8 +5,7 @@ rename com/.*/nano/ devicenano/ rename android/.*/nano/ devicenano/ - -# StatsD autogenerated classes. Maybe add a heuristic? +# StatsD auto-generated class com.android.internal.util.FrameworkStatsLog keepclass # Exported to Mainline modules; cannot use annotations diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index cb4e9949a8ff..2808056f72c9 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -175,10 +175,13 @@ flag { } flag { - name: "magnification_enlarge_pointer" + name: "magnification_enlarge_pointer_bugfix" namespace: "accessibility" description: "When fullscreen magnification is enabled, pointer icon is enlarged" bug: "355734856" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index ce1a292fb069..d3d80e12313f 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -1759,7 +1759,7 @@ public class FullScreenMagnificationController implements * @param scale The new scale factor. */ public void notifyScaleForInput(int displayId, float scale) { - if (Flags.magnificationEnlargePointer()) { + if (Flags.magnificationEnlargePointerBugfix()) { mControllerCtx.getInputManager() .setAccessibilityPointerIconScaleFactor(displayId, scale); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 591107010431..51034d24df14 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -628,16 +628,25 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void enablePermissionsSync(int associationId) { + if (getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system UID"); + } mSystemDataTransferProcessor.enablePermissionsSync(associationId); } @Override public void disablePermissionsSync(int associationId) { + if (getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system UID"); + } mSystemDataTransferProcessor.disablePermissionsSync(associationId); } @Override public PermissionSyncRequest getPermissionSyncRequest(int associationId) { + if (getCallingUid() != SYSTEM_UID) { + throw new SecurityException("Caller must be system UID"); + } return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId); } diff --git a/services/core/Android.bp b/services/core/Android.bp index c73846350b40..348d83fea81d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -50,7 +50,7 @@ java_library_static { ], } -genrule { +java_genrule { name: "services.core.protologsrc", srcs: [ ":protolog-impl", @@ -70,7 +70,7 @@ genrule { out: ["services.core.protolog.srcjar"], } -genrule { +java_genrule { name: "generate-protolog.json", srcs: [ ":protolog-groups", @@ -87,7 +87,7 @@ genrule { out: ["services.core.protolog.json"], } -genrule { +java_genrule { name: "gen-core.protolog.pb", srcs: [ ":protolog-groups", @@ -284,7 +284,7 @@ prebuilt_etc { src: "java/com/android/server/location/gnss/gps_debug.conf", } -genrule { +java_genrule { name: "services.core.json.gz", srcs: [":generate-protolog.json"], out: ["services.core.protolog.json.gz"], diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 37dddc61a976..c15cf34b8955 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -48,3 +48,6 @@ per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS # CertBlocklister per-file Cert*.java = tweek@google.com, brambonne@google.com, prb@google.com, miguelaranda@google.com + +# TradeInMode +per-file TradeInModeService.java = dvander@google.com, paullawrence@google.com diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 39ac5150c7f1..363807d2aa8c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -68,6 +68,7 @@ import android.telephony.CellSignalStrengthWcdma; import android.telephony.DisconnectCause; import android.telephony.LinkCapacityEstimate; import android.telephony.LocationAccessPolicy; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; import android.telephony.PhysicalChannelConfig; @@ -90,6 +91,7 @@ import android.telephony.ims.MediaQualityStatus; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.LocalLog; import android.util.Pair; import android.util.SparseArray; @@ -429,6 +431,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean[] mCarrierRoamingNtnMode = null; private boolean[] mCarrierRoamingNtnEligible = null; + private List<IntArray> mCarrierRoamingNtnAvailableServices; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -741,6 +745,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mCarrierServiceStates, mNumPhones); cutListToSize(mCallStateLists, mNumPhones); cutListToSize(mMediaQualityStatus, mNumPhones); + cutListToSize(mCarrierRoamingNtnAvailableServices, mNumPhones); return; } @@ -789,6 +794,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; + mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); } } } @@ -864,6 +870,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration = new long[numPhones]; mCarrierRoamingNtnMode = new boolean[numPhones]; mCarrierRoamingNtnEligible = new boolean[numPhones]; + mCarrierRoamingNtnAvailableServices = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; @@ -909,6 +916,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; + mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1533,6 +1541,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if (events.contains( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED)) { + try { + r.callback.onCarrierRoamingNtnAvailableServicesChanged( + mCarrierRoamingNtnAvailableServices.get(r.phoneId).toArray()); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } @@ -3642,6 +3659,47 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Notify external listeners that carrier roaming non-terrestrial available services changed. + * @param availableServices The list of the supported services. + */ + public void notifyCarrierRoamingNtnAvailableServicesChanged( + int subId, @NetworkRegistrationInfo.ServiceType int[] availableServices) { + if (!checkNotifyPermission("notifyCarrierRoamingNtnEligibleStateChanged")) { + log("notifyCarrierRoamingNtnAvailableServicesChanged: caller does not have required " + + "permissions."); + return; + } + + if (VDBG) { + log("notifyCarrierRoamingNtnAvailableServicesChanged: " + + "availableServices=" + Arrays.toString(availableServices)); + } + + synchronized (mRecords) { + int phoneId = getPhoneIdFromSubId(subId); + if (!validatePhoneId(phoneId)) { + loge("Invalid phone ID " + phoneId + " for " + subId); + return; + } + IntArray availableServicesIntArray = new IntArray(availableServices.length); + availableServicesIntArray.addAll(availableServices); + mCarrierRoamingNtnAvailableServices.set(phoneId, availableServicesIntArray); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED) + && idMatch(r, subId, phoneId)) { + try { + r.callback.onCarrierRoamingNtnAvailableServicesChanged(availableServices); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + @NeverCompile // Avoid size overhead of debugging code. @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -3706,6 +3764,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Pair<String, Integer> carrierServiceState = mCarrierServiceStates.get(i); pw.println("mCarrierServiceState=<package=" + pii(carrierServiceState.first) + ", uid=" + carrierServiceState.second + ">"); + pw.println("mCarrierRoamingNtnAvailableServices=" + + mCarrierRoamingNtnAvailableServices.get(i)); pw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java new file mode 100644 index 000000000000..9ad550b6caf9 --- /dev/null +++ b/services/core/java/com/android/server/TradeInModeService.java @@ -0,0 +1,342 @@ +/* + * 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; + +import static com.android.tradeinmode.flags.Flags.enableTradeInMode; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.OnAccountsUpdateListener; +import android.annotation.RequiresPermission; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.Uri; +import android.os.Binder; +import android.os.ITradeInMode; +import android.os.SystemProperties; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.service.persistentdata.PersistentDataBlockManager; +import android.util.Slog; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + + +public final class TradeInModeService extends SystemService { + private static final String TAG = "TradeInModeService"; + + private static final String TIM_PROP = "persist.adb.tradeinmode"; + + private static final int TIM_STATE_UNSET = 0; + + // adbd_tradeinmode was stopped. + private static final int TIM_STATE_DISABLED = -1; + + // adbd_tradeinmode has started. + private static final int TIM_STATE_FOYER = 1; + + // Full non-root adb granted; factory reset is guaranteed. + private static final int TIM_STATE_EVALUATION_MODE = 2; + + // This file contains a single integer counter of how many boot attempts + // have been made since entering evaluation mode. + private static final String WIPE_INDICATOR_FILE = "/metadata/tradeinmode/wipe"; + + private final Context mContext; + private TradeInMode mTradeInMode; + + private ConnectivityManager mConnectivityManager; + private ConnectivityManager.NetworkCallback mNetworkCallback = null; + + private AccountManager mAccountManager; + private OnAccountsUpdateListener mAccountsListener = null; + + public TradeInModeService(Context context) { + super(context); + + mContext = context; + } + + @Override + public void onStart() { + if (!enableTradeInMode()) { + return; + } + + mTradeInMode = new TradeInMode(); + publishBinderService("tradeinmode", mTradeInMode); + } + + @Override + public void onBootPhase(@BootPhase int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + final int state = getTradeInModeState(); + + if (isAdbEnabled() && !isDebuggable() && !isDeviceSetup() + && state == TIM_STATE_DISABLED) { + // If we fail to start trade-in mode, the persist property may linger + // past reboot. If we detect this, disable ADB and clear TIM state. + Slog.i(TAG, "Resetting trade-in mode state."); + SystemProperties.set(TIM_PROP, ""); + + final ContentResolver cr = mContext.getContentResolver(); + Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0); + } else if (state == TIM_STATE_FOYER) { + // If zygote crashed or we rebooted, and TIM is still enabled, make + // sure it's allowed to be enabled. If it is, we need to re-add our + // setup completion observer. + if (isDeviceSetup()) { + stopTradeInMode(); + } else { + watchForSetupCompletion(); + } + } + } + } + + private final class TradeInMode extends ITradeInMode.Stub { + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public boolean start() { + mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE", + "Cannot enter trade-in mode foyer"); + final int state = getTradeInModeState(); + if (state == TIM_STATE_FOYER) { + return true; + } + + if (state != TIM_STATE_UNSET) { + Slog.e(TAG, "Cannot enter trade-in mode in state: " + state); + return false; + } + + if (isDeviceSetup()) { + Slog.i(TAG, "Not starting trade-in mode, device is setup."); + return false; + } + if (SystemProperties.getInt("ro.debuggable", 0) == 1) { + // We don't want to force adbd into TIM on debug builds. + Slog.e(TAG, "Not starting trade-in mode, device is debuggable."); + return false; + } + if (isAdbEnabled()) { + Slog.e(TAG, "Not starting trade-in mode, adb is already enabled."); + return false; + } + + final long callingId = Binder.clearCallingIdentity(); + try { + startTradeInMode(); + } finally { + Binder.restoreCallingIdentity(callingId); + } + return true; + } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public boolean enterEvaluationMode() { + mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE", + "Cannot enter trade-in evaluation mode"); + final int state = getTradeInModeState(); + if (state != TIM_STATE_FOYER) { + Slog.e(TAG, "Cannot enter evaluation mode in state: " + state); + return false; + } + if (isFrpActive()) { + Slog.e(TAG, "Cannot enter evaluation mode, FRP lock is present."); + return false; + } + + try (FileWriter fw = new FileWriter(WIPE_INDICATOR_FILE, + StandardCharsets.US_ASCII)) { + fw.write("0"); + } catch (IOException e) { + Slog.e(TAG, "Failed to write " + WIPE_INDICATOR_FILE, e); + return false; + } + + final long callingId = Binder.clearCallingIdentity(); + try { + removeNetworkWatch(); + removeAccountsWatch(); + } finally { + Binder.restoreCallingIdentity(callingId); + } + + SystemProperties.set(TIM_PROP, Integer.toString(TIM_STATE_EVALUATION_MODE)); + SystemProperties.set("ctl.restart", "adbd"); + return true; + } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public boolean isEvaluationModeAllowed() { + mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE", + "Cannot test for trade-in evaluation mode allowed"); + return !isFrpActive(); + } + } + + private void startTradeInMode() { + Slog.i(TAG, "Enabling trade-in mode."); + + SystemProperties.set(TIM_PROP, Integer.toString(TIM_STATE_FOYER)); + + final ContentResolver cr = mContext.getContentResolver(); + Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1); + + watchForSetupCompletion(); + watchForNetworkChange(); + watchForAccountsCreated(); + } + + private void stopTradeInMode() { + Slog.i(TAG, "Stopping trade-in mode."); + + SystemProperties.set(TIM_PROP, Integer.toString(TIM_STATE_DISABLED)); + + removeNetworkWatch(); + removeAccountsWatch(); + + final ContentResolver cr = mContext.getContentResolver(); + Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0); + } + + private int getTradeInModeState() { + return SystemProperties.getInt(TIM_PROP, TIM_STATE_UNSET); + } + + private boolean isDebuggable() { + return SystemProperties.getInt("ro.debuggable", 0) == 1; + } + + private boolean isAdbEnabled() { + final ContentResolver cr = mContext.getContentResolver(); + return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1; + } + + private boolean isFrpActive() { + try { + PersistentDataBlockManager pdb = + mContext.getSystemService(PersistentDataBlockManager.class); + if (pdb == null) { + return false; + } + return pdb.isFactoryResetProtectionActive(); + } catch (Exception e) { + Slog.e(TAG, "Could not read PDB", e); + return false; + } + } + + // This returns true if the device has progressed far enough into Setup Wizard that it no + // longer makes sense to enable trade-in mode. As a last stop, we check the SUW completion + // bits. + private boolean isDeviceSetup() { + final ContentResolver cr = mContext.getContentResolver(); + try { + if (Settings.Secure.getIntForUser(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) { + return true; + } + } catch (SettingNotFoundException e) { + Slog.e(TAG, "Could not find USER_SETUP_COMPLETE setting", e); + } + + if (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) { + return true; + } + return false; + } + + private final class SettingsObserver extends ContentObserver { + SettingsObserver() { + super(null); + } + + @Override + public void onChange(boolean selfChange) { + if (getTradeInModeState() == TIM_STATE_FOYER && isDeviceSetup()) { + stopTradeInMode(); + } + } + } + + private void watchForSetupCompletion() { + final Uri userSetupComplete = Settings.Secure.getUriFor( + Settings.Secure.USER_SETUP_COMPLETE); + final Uri deviceProvisioned = Settings.Global.getUriFor( + Settings.Global.DEVICE_PROVISIONED); + final ContentResolver cr = mContext.getContentResolver(); + final SettingsObserver observer = new SettingsObserver(); + + cr.registerContentObserver(userSetupComplete, false, observer); + cr.registerContentObserver(deviceProvisioned, false, observer); + } + + + private void watchForNetworkChange() { + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); + NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + mNetworkCallback = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + super.onAvailable(network); + stopTradeInMode(); + } + }; + + mConnectivityManager.registerNetworkCallback(networkRequest, mNetworkCallback); + } + + private void removeNetworkWatch() { + if (mNetworkCallback != null) { + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mNetworkCallback = null; + } + } + + private void watchForAccountsCreated() { + mAccountManager = mContext.getSystemService(AccountManager.class); + mAccountsListener = new OnAccountsUpdateListener() { + @Override + public void onAccountsUpdated(Account[] accounts) { + stopTradeInMode(); + } + }; + mAccountManager.addOnAccountsUpdatedListener(mAccountsListener, null, false); + } + + private void removeAccountsWatch() { + if (mAccountsListener != null) { + mAccountManager.removeOnAccountsUpdatedListener(mAccountsListener); + mAccountsListener = null; + } + } +} diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 95dbaae2c43b..8e520dc632c3 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -290,7 +290,7 @@ public class Watchdog implements Dumpable { public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) { mWaitMaxMillis = handlerCheckerTimeoutMillis; - if (mCompleted) { + if (mCompleted && !mMonitorQueue.isEmpty()) { // Safe to update monitors in queue, Handler is not in the middle of work mMonitors.addAll(mMonitorQueue); mMonitorQueue.clear(); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 0ca3b56486e3..679c7ac3ceac 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -112,6 +112,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.modules.expresslog.Histogram; @@ -1226,7 +1227,17 @@ public class AccountManagerService // been re-enabled (after being updated for example), then we just overwrite the old // values. for (Entry<String, Integer> entry : knownAuth.entrySet()) { - accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue()); + String type = entry.getKey(); + Integer newUid = entry.getValue(); + if (!Objects.equals(metaAuthUid.get(type), newUid)) { + FrameworkStatsLog.write( + FrameworkStatsLog.ACCOUNT_MANAGER_EVENT, + type, + newUid, + FrameworkStatsLog + .ACCOUNT_MANAGER_EVENT__EVENT_TYPE__AUTHENTICATOR_ADDED); + } + accountsDb.insertOrReplaceMetaAuthTypeAndUid(type, newUid); } final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts(); @@ -1945,6 +1956,11 @@ public class AccountManagerService } accounts.accountsDb.setTransactionSuccessful(); + FrameworkStatsLog.write( + FrameworkStatsLog.ACCOUNT_MANAGER_EVENT, + account.type, + callingUid, + FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_ADDED); logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid); @@ -2544,6 +2560,11 @@ public class AccountManagerService } String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE; + FrameworkStatsLog.write( + FrameworkStatsLog.ACCOUNT_MANAGER_EVENT, + account.type, + callingUid, + FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_REMOVED); logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 65a2c187f1c8..4944cafeb83d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -163,6 +163,7 @@ final class ActivityManagerConstants extends ContentObserver { static final String KEY_USE_TIERED_CACHED_ADJ = "use_tiered_cached_adj"; static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time"; + static final String KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE = "tiered_cached_adj_ui_tier_size"; /** * Whether or not to enable the new oom adjuster implementation. @@ -248,6 +249,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers(); private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000; + private static final int TIERED_CACHED_ADJ_MAX_UI_TIER_SIZE = 50; + private final int mDefaultTieredCachedAdjUiTierSize; /** * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}. @@ -1154,6 +1157,9 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_TIERED_CACHED_ADJ_DECAY_TIME */ public long TIERED_CACHED_ADJ_DECAY_TIME = DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME; + /** @see #KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE */ + public int TIERED_CACHED_ADJ_UI_TIER_SIZE; + /** @see #KEY_ENABLE_NEW_OOMADJ */ public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ; @@ -1363,6 +1369,7 @@ final class ActivityManagerConstants extends ContentObserver { break; case KEY_USE_TIERED_CACHED_ADJ: case KEY_TIERED_CACHED_ADJ_DECAY_TIME: + case KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE: updateUseTieredCachedAdj(); break; case KEY_DISABLE_APP_PROFILER_PSS_PROFILING: @@ -1466,6 +1473,11 @@ final class ActivityManagerConstants extends ContentObserver { mDefaultPssToRssThresholdModifier = context.getResources().getFloat( com.android.internal.R.dimen.config_am_pssToRssThresholdModifier); PSS_TO_RSS_THRESHOLD_MODIFIER = mDefaultPssToRssThresholdModifier; + + mDefaultTieredCachedAdjUiTierSize = context.getResources().getInteger( + com.android.internal.R.integer.config_am_tieredCachedAdjUiTierSize); + TIERED_CACHED_ADJ_UI_TIER_SIZE = Math.min( + mDefaultTieredCachedAdjUiTierSize, TIERED_CACHED_ADJ_MAX_UI_TIER_SIZE); } public void start(ContentResolver resolver) { @@ -2255,6 +2267,12 @@ final class ActivityManagerConstants extends ContentObserver { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_TIERED_CACHED_ADJ_DECAY_TIME, DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME); + TIERED_CACHED_ADJ_UI_TIER_SIZE = Math.min( + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE, + mDefaultTieredCachedAdjUiTierSize), + TIERED_CACHED_ADJ_MAX_UI_TIER_SIZE); } private void updateEnableNewOomAdj() { @@ -2510,6 +2528,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(USE_TIERED_CACHED_ADJ); pw.print(" "); pw.print(KEY_TIERED_CACHED_ADJ_DECAY_TIME); pw.print("="); pw.println(TIERED_CACHED_ADJ_DECAY_TIME); + pw.print(" "); pw.print(KEY_TIERED_CACHED_ADJ_UI_TIER_SIZE); + pw.print("="); pw.println(TIERED_CACHED_ADJ_UI_TIER_SIZE); pw.print(" "); pw.print(KEY_ENABLE_NEW_OOMADJ); pw.print("="); pw.println(ENABLE_NEW_OOMADJ); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6ba851423219..87ce649474a5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -632,8 +632,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE"; static final String EXTRA_BUGREPORT_NONCE = "android.intent.extra.BUGREPORT_NONCE"; - static final String EXTRA_EXTRA_ATTACHMENT_URI = - "android.intent.extra.EXTRA_ATTACHMENT_URI"; + static final String EXTRA_EXTRA_ATTACHMENT_URIS = + "android.intent.extra.EXTRA_ATTACHMENT_URIS"; /** * The maximum number of bytes that {@link #setProcessStateSummary} accepts. @@ -2772,8 +2772,12 @@ public class ActivityManagerService extends IActivityManager.Stub // Add common services. // IMPORTANT: Before adding services here, make sure ephemeral apps can access them too. // Enable the check in ApplicationThread.bindApplication() to make sure. - if (!android.server.Flags.removeJavaServiceManagerCache()) { - addServiceToMap(mAppBindArgs, "permissionmgr"); + + // Removing User Service and App Ops Service from cache breaks boot for auto. + // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions. + // TODO: fix SELinux restrictions and remove caching for Android Auto. + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + || !android.server.Flags.removeJavaServiceManagerCache()) { addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE); addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE); addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE); @@ -2782,16 +2786,17 @@ public class ActivityManagerService extends IActivityManager.Stub addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE); addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE); addServiceToMap(mAppBindArgs, "graphicsstats"); - addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE); addServiceToMap(mAppBindArgs, "content"); addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE); addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE); addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE); addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE); addServiceToMap(mAppBindArgs, Context.POWER_SERVICE); - addServiceToMap(mAppBindArgs, Context.USER_SERVICE); addServiceToMap(mAppBindArgs, "mount"); addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE); + addServiceToMap(mAppBindArgs, "permissionmgr"); + addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE); + addServiceToMap(mAppBindArgs, Context.USER_SERVICE); } // See b/79378449 // Getting the window service and package service binder from servicemanager @@ -5550,6 +5555,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (target instanceof PendingIntentRecord) { final PendingIntentRecord originalRecord = (PendingIntentRecord) target; + addCreatorToken(intent, originalRecord.getPackageName()); + // In multi-display scenarios, there can be background users who execute the // PendingIntent. In these scenarios, we don't want to use the foreground user as the // current user. @@ -7660,7 +7667,7 @@ public class ActivityManagerService extends IActivityManager.Stub */ public void requestBugReportWithDescription(@Nullable String shareTitle, @Nullable String shareDescription, int bugreportType, long nonce, - @Nullable Uri extraAttachment) { + @Nullable List<Uri> extraAttachments) { String type = null; switch (bugreportType) { case BugreportParams.BUGREPORT_MODE_FULL: @@ -7715,8 +7722,9 @@ public class ActivityManagerService extends IActivityManager.Stub triggerShellBugreport.setPackage(SHELL_APP_PACKAGE); triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType); triggerShellBugreport.putExtra(EXTRA_BUGREPORT_NONCE, nonce); - if (extraAttachment != null) { - triggerShellBugreport.putExtra(EXTRA_EXTRA_ATTACHMENT_URI, extraAttachment); + if (extraAttachments != null && !extraAttachments.isEmpty()) { + triggerShellBugreport.putParcelableArrayListExtra(EXTRA_EXTRA_ATTACHMENT_URIS, + new ArrayList(extraAttachments)); triggerShellBugreport.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -7775,9 +7783,9 @@ public class ActivityManagerService extends IActivityManager.Stub * Takes an interactive bugreport with a progress notification. Also attaches given file uri. */ @Override - public void requestBugReportWithExtraAttachment(@NonNull Uri extraAttachment) { + public void requestBugReportWithExtraAttachments(@NonNull List<Uri> extraAttachments) { requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_INTERACTIVE, 0L, - extraAttachment); + extraAttachments); } /** diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 28b606c931fa..7c563abea22f 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -62,6 +62,7 @@ import android.os.BatteryUsageStatsQuery; import android.os.Binder; import android.os.BluetoothBatteryStats; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -213,6 +214,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); + private final ConditionVariable mSystemReady = new ConditionVariable(false); private final Object mPowerStatsLock = new Object(); @GuardedBy("mPowerStatsLock") @@ -413,6 +415,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandlerThread = new HandlerThread("batterystats-handler"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); + mHandler.post(mSystemReady::block); mMonotonicClock = new MonotonicClock(new File(systemDir, "monotonic_clock.xml")); mPowerProfile = new PowerProfile(context); @@ -443,8 +446,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mPowerStatsStore = new PowerStatsStore(systemDir, mHandler); mPowerAttributor = new MultiStatePowerAttributor(mContext, mPowerStatsStore, mPowerProfile, - mCpuScalingPolicies, () -> mStats.getBatteryCapacity(), - mPowerStatsUidResolver); + mCpuScalingPolicies, () -> mStats.getBatteryCapacity()); mPowerStatsScheduler = createPowerStatsScheduler(mContext); int accumulatedBatteryUsageStatsSpanSize = mContext.getResources().getInteger( @@ -648,6 +650,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub dataConnectionStats.startMonitoring(); registerStatsCallbacks(); + mSystemReady.open(); } private static boolean isBatteryUsageStatsAccumulationSupported() { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 70517145575a..8d16eb5fc95a 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1149,6 +1149,8 @@ public class OomAdjuster { if (mConstants.USE_TIERED_CACHED_ADJ) { final long now = mInjector.getUptimeMillis(); int uiTargetAdj = 10; + // mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE is 10 by default, but is configurable. + final int uiTierMaxAdj = 10 + mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE; for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; @@ -1162,17 +1164,17 @@ public class OomAdjuster { if (opt != null && opt.isFreezeExempt()) { // BIND_WAIVE_PRIORITY and the like get oom_adj 900 targetAdj += 0; - } else if (state.hasShownUi() && uiTargetAdj < 20) { - // The most recent 10 apps that have shown UI get 910-919 + } else if (state.hasShownUi() && uiTargetAdj < uiTierMaxAdj) { + // The most recent UI-showing apps get [910, 910 + ui tier size). targetAdj += uiTargetAdj++; } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ) && (state.getLastStateTime() + mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) { - // Older cached apps get 950 - targetAdj += 50; + // Older cached apps get 940 + ui tier size (950 by default). + targetAdj += 40 + mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE; } else { - // Newer cached apps get 920 - targetAdj += 20; + // Newer cached apps get 910 + ui tier size (920 by default). + targetAdj += 10 + mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE; } state.setCurRawAdj(targetAdj); state.setCurAdj(psr.modifyRawOomAdj(targetAdj)); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 2f4e8bbea1d7..7afcb130c54d 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -176,6 +176,7 @@ public class SettingsToPropertiesMapper { "core_libraries", "crumpet", "dck_framework", + "desktop_stats", "devoptions_settings", "game", "gpu", diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index e97629bfd3e1..b4cce7da4416 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -71,6 +71,7 @@ import static android.content.Intent.EXTRA_REPLACING; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP; import static android.permission.flags.Flags.deviceAwareAppOpNewSchemaEnabled; +import static android.permission.flags.Flags.checkOpValidatePackage; import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED; import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION; @@ -2860,20 +2861,30 @@ public class AppOpsService extends IAppOpsService.Stub { private int checkOperationImpl(int code, int uid, String packageName, @Nullable String attributionTag, int virtualDeviceId, boolean raw) { - verifyIncomingOp(code); - if (!isValidVirtualDeviceId(virtualDeviceId)) { - Slog.w(TAG, - "checkOperationImpl returned MODE_IGNORED as virtualDeviceId " + virtualDeviceId - + " is invalid"); - return AppOpsManager.MODE_IGNORED; - } - if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { - return AppOpsManager.opToDefaultMode(code); - } + String resolvedPackageName; + if (!shouldUseNewCheckOp()) { + verifyIncomingOp(code); + if (!isValidVirtualDeviceId(virtualDeviceId)) { + Slog.w(TAG, "checkOperationImpl returned MODE_IGNORED as virtualDeviceId " + + virtualDeviceId + " is invalid"); + return AppOpsManager.MODE_IGNORED; + } + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return AppOpsManager.opToDefaultMode(code); + } - String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); - if (resolvedPackageName == null) { - return AppOpsManager.MODE_IGNORED; + resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } + } else { + // Note, this flag changes the behavior in this case: invalid packages now don't + // succeed checkOp + resolvedPackageName = validateOpRequest(code, uid, packageName, + virtualDeviceId, false, "checkOperation"); + if (resolvedPackageName == null) { + return AppOpsManager.MODE_IGNORED; + } } return checkOperationUnchecked(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, raw); @@ -3147,25 +3158,37 @@ public class AppOpsService extends IAppOpsService.Stub { @Nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp, @Nullable String message, boolean shouldCollectMessage) { - verifyIncomingUid(uid); - verifyIncomingOp(code); - if (!isValidVirtualDeviceId(virtualDeviceId)) { - Slog.w(TAG, - "checkOperationImpl returned MODE_IGNORED as virtualDeviceId " + virtualDeviceId - + " is invalid"); - return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, - packageName); - } - if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { - return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, - packageName); - } + String resolvedPackageName; + if (!shouldUseNewCheckOp()) { + verifyIncomingUid(uid); + verifyIncomingOp(code); + if (!isValidVirtualDeviceId(virtualDeviceId)) { + Slog.w(TAG, "checkOperationImpl returned MODE_IGNORED as virtualDeviceId " + + virtualDeviceId + " is invalid"); + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, + packageName); + } - String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); - if (resolvedPackageName == null) { - return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, - packageName); + resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } + } else { + // Note, this flag changes the behavior in this case: + // invalid package is now IGNORE instead of ERROR for consistency + resolvedPackageName = validateOpRequest(code, uid, packageName, + virtualDeviceId, true, "noteOperation"); + if (resolvedPackageName == null) { + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } } + return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag, virtualDeviceId, Process.INVALID_UID, null, null, Context.DEVICE_ID_DEFAULT, AppOpsManager.OP_FLAG_SELF, shouldCollectAsyncNotedOp, @@ -3602,24 +3625,35 @@ public class AppOpsService extends IAppOpsService.Stub { boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags, int attributionChainId) { - verifyIncomingUid(uid); - verifyIncomingOp(code); - if (!isValidVirtualDeviceId(virtualDeviceId)) { - Slog.w(TAG, - "startOperationImpl returned MODE_IGNORED as virtualDeviceId " + virtualDeviceId - + " is invalid"); - return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, - packageName); - } - if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { - return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, - packageName); - } + String resolvedPackageName; + if (!shouldUseNewCheckOp()) { + verifyIncomingUid(uid); + verifyIncomingOp(code); + if (!isValidVirtualDeviceId(virtualDeviceId)) { + Slog.w(TAG, "startOperationImpl returned MODE_IGNORED as virtualDeviceId " + + virtualDeviceId + " is invalid"); + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, + packageName); + } - String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); - if (resolvedPackageName == null) { - return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, - packageName); + resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } + } else { + // Note, this flag changes the behavior in this case: + // invalid package is now IGNORE instead of ERROR for consistency + resolvedPackageName = validateOpRequest(code, uid, packageName, + virtualDeviceId, true, "startOperation"); + if (resolvedPackageName == null) { + return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag, + packageName); + } } // As a special case for OP_RECORD_AUDIO_HOTWORD, OP_RECEIVE_AMBIENT_TRIGGER_AUDIO and @@ -4341,6 +4375,48 @@ public class AppOpsService extends IAppOpsService.Stub { || (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0; } + private boolean shouldUseNewCheckOp() { + final long identity = Binder.clearCallingIdentity(); + try { + return checkOpValidatePackage(); + } catch (Exception e) { + // before device provider init, only on old storage + return true; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Validates arguments for a particular op request + * @param shouldVerifyUid - If the calling uid needs perms for other uids, due to the method + * being an appop write. + * @param methodName - For logging purposes + * @return The resolved package for the request, null on any failure + */ + private @Nullable String validateOpRequest(int code, int uid, String packageName, int vdi, + boolean shouldVerifyUid, String methodName) { + verifyIncomingOp(code); + if (shouldVerifyUid) { + verifyIncomingUid(uid); + } + if (!isValidVirtualDeviceId(vdi)) { + Slog.w(TAG, methodName + ": error due to virtualDeviceId " + vdi + " is invalid"); + return null; + } + if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) { + Slog.w(TAG, methodName + ": error due to package: " + packageName + + " is invalid for " + uid); + return null; + } + String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName); + if (resolvedPackageName == null) { + Slog.w(TAG, methodName + ": error due to unable to resolve uid: " + uid); + return null; + } + return resolvedPackageName; + } + private void verifyIncomingProxyUid(@NonNull AttributionSource attributionSource) { if (attributionSource.getUid() == Binder.getCallingUid()) { return; @@ -4995,17 +5071,7 @@ public class AppOpsService extends IAppOpsService.Stub { } success = true; - } catch (IllegalStateException e) { - Slog.w(TAG, "Failed parsing " + e); - } catch (NullPointerException e) { - Slog.w(TAG, "Failed parsing " + e); - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed parsing " + e); - } catch (XmlPullParserException e) { - Slog.w(TAG, "Failed parsing " + e); - } catch (IOException e) { - Slog.w(TAG, "Failed parsing " + e); - } catch (IndexOutOfBoundsException e) { + } catch (Exception e) { Slog.w(TAG, "Failed parsing " + e); } finally { if (!success) { diff --git a/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java b/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java index 9ed3a99013ea..b677a1dfc84b 100644 --- a/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java +++ b/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java @@ -50,6 +50,10 @@ class LegacyAppOpStateParser { public int readState(AtomicFile file, SparseArray<SparseIntArray> uidModes, SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes) { try (FileInputStream stream = file.openRead()) { + SparseArray<SparseIntArray> parsedUidModes = new SparseArray<>(); + SparseArray<ArrayMap<String, SparseIntArray>> parsedUserPackageModes = + new SparseArray<>(); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -75,26 +79,37 @@ class LegacyAppOpStateParser { // version 2 has the structure pkg -> uid -> op -> // in version 3, since pkg and uid states are kept completely // independent we switch to user -> pkg -> op - readPackage(parser, userPackageModes); + readPackage(parser, parsedUserPackageModes); } else if (tagName.equals("uid")) { - readUidOps(parser, uidModes); + readUidOps(parser, parsedUidModes); } else if (tagName.equals("user")) { - readUser(parser, userPackageModes); + readUser(parser, parsedUserPackageModes); } else { Slog.w(TAG, "Unknown element under <app-ops>: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } + + // Parsing is complete, copy all parsed values to output + final int parsedUidModesSize = parsedUidModes.size(); + for (int i = 0; i < parsedUidModesSize; i++) { + uidModes.put(parsedUidModes.keyAt(i), parsedUidModes.valueAt(i)); + } + final int parsedUserPackageModesSize = parsedUserPackageModes.size(); + for (int i = 0; i < parsedUserPackageModesSize; i++) { + userPackageModes.put(parsedUserPackageModes.keyAt(i), + parsedUserPackageModes.valueAt(i)); + } + return versionAtBoot; } catch (FileNotFoundException e) { Slog.i(TAG, "No existing app ops " + file.getBaseFile() + "; starting empty"); - return NO_FILE_VERSION; - } catch (XmlPullParserException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); + } catch (Exception e) { + // All exceptions must be caught, otherwise device will not be able to boot + Slog.wtf(TAG, "Failed parsing " + e); } + return NO_FILE_VERSION; } private void readPackage(TypedXmlPullParser parser, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 78a1fa768879..bfef6855fe7e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -68,10 +68,11 @@ import static com.android.media.audio.Flags.audioserverPermissions; import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume; import static com.android.media.audio.Flags.equalScoLeaVcIndexRange; import static com.android.media.audio.Flags.replaceStreamBtSco; -import static com.android.media.audio.Flags.ringerModeAffectsAlarm; import static com.android.media.audio.Flags.ringMyCar; +import static com.android.media.audio.Flags.ringerModeAffectsAlarm; import static com.android.media.audio.Flags.setStreamVolumeOrder; import static com.android.media.audio.Flags.vgsVssSyncMuteOrder; +import static com.android.media.flags.Flags.enableAudioInputDeviceRoutingAndVolumeControl; import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE; import static com.android.server.utils.EventLogger.Event.ALOGE; import static com.android.server.utils.EventLogger.Event.ALOGI; @@ -491,6 +492,10 @@ public class AudioService extends IAudioService.Stub private static final int MSG_INIT_SPATIALIZER = 102; private static final int MSG_INIT_ADI_DEVICE_STATES = 103; + private static final int MSG_INIT_INPUT_GAINS = 104; + private static final int MSG_SET_INPUT_GAIN_INDEX = 105; + private static final int MSG_PERSIST_INPUT_GAIN_INDEX = 106; + // end of messages handled under wakelock // retry delay in case of failure to indicate system ready to AudioFlinger @@ -512,6 +517,11 @@ public class AudioService extends IAudioService.Stub **/ private SparseArray<VolumeStreamState> mStreamStates; + /** + * @see InputDeviceVolumeHelper + */ + private InputDeviceVolumeHelper mInputDeviceVolumeHelper; + /*package*/ int getVssVolumeForDevice(int stream, int device) { final VolumeStreamState streamState = mStreamStates.get(stream); return streamState != null ? streamState.getIndex(device) : -1; @@ -1501,6 +1511,15 @@ public class AudioService extends IAudioService.Stub 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER, 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */); + if (enableAudioInputDeviceRoutingAndVolumeControl()) { + queueMsgUnderWakeLock( + mAudioHandler, + MSG_INIT_INPUT_GAINS, + 0 /* arg1 */, + 0 /* arg2 */, + null /* obj */, + 0 /* delay */); + } mDisplayManager = context.getSystemService(DisplayManager.class); @@ -1594,6 +1613,16 @@ public class AudioService extends IAudioService.Stub } } + /** Called by handling of MSG_INIT_INPUT_GAINS */ + private void onInitInputGains() { + mInputDeviceVolumeHelper = + new InputDeviceVolumeHelper( + mSettings, + mContentResolver, + mSettingsLock, + System.INPUT_GAIN_INDEX_SETTINGS); + } + private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() { @Override @@ -5742,6 +5771,90 @@ public class AudioService extends IAudioService.Stub : aliasStreamType == sStreamVolumeAlias.get(AudioSystem.STREAM_SYSTEM); } + /** + * @see AudioDeviceVolumeManager#setInputGainIndex(AudioDeviceAttributes, int) + */ + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public void setInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) { + super.setInputGainIndex_enforcePermission(); + + if (mInputDeviceVolumeHelper.setInputGainIndex(ada, index)) { + // Post message to set system volume (it in turn will post a message + // to persist). + sendMsg( + mAudioHandler, + MSG_SET_INPUT_GAIN_INDEX, + SENDMSG_QUEUE, + /*arg1*/ index, + /*arg2*/ 0, + /*obj*/ ada, + /*delay*/ 0); + } + } + + private void setInputGainIndexInt(@NonNull AudioDeviceAttributes ada, int index) { + // TODO(b/364923030): call AudioSystem to apply input gain in native layer. + + // Post a persist input gain msg. + sendMsg( + mAudioHandler, + MSG_PERSIST_INPUT_GAIN_INDEX, + SENDMSG_QUEUE, + /*arg1*/ index, + /*arg2*/ 0, + /*obj*/ ada, + PERSIST_DELAY); + } + + private void persistInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) { + mInputDeviceVolumeHelper.persistInputGainIndex(ada, index); + } + + /** + * @see AudioDeviceVolumeManager#getInputGainIndex(AudioDeviceAttributes) + */ + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getInputGainIndex(@NonNull AudioDeviceAttributes ada) { + super.getInputGainIndex_enforcePermission(); + + return mInputDeviceVolumeHelper.getInputGainIndex(ada); + } + + /** + * @see AudioDeviceVolumeManager#getMaxInputGainIndex() + */ + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getMaxInputGainIndex() { + super.getMaxInputGainIndex_enforcePermission(); + + return mInputDeviceVolumeHelper.getMaxInputGainIndex(); + } + + /** + * @see AudioDeviceVolumeManager#getMinInputGainIndex() + */ + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public int getMinInputGainIndex() { + super.getMinInputGainIndex_enforcePermission(); + + return mInputDeviceVolumeHelper.getMinInputGainIndex(); + } + + /** + * @see AudioDeviceVolumeManager#isInputGainFixed(AudioDeviceAttributes) + */ + @Override + @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED) + public boolean isInputGainFixed(@NonNull AudioDeviceAttributes ada) { + super.isInputGainFixed_enforcePermission(); + + return mInputDeviceVolumeHelper.isInputGainFixed(ada); + } + /** @see AudioManager#setMicrophoneMute(boolean) */ @Override public void setMicrophoneMute(boolean on, String callingPackage, int userId, @@ -10077,6 +10190,14 @@ public class AudioService extends IAudioService.Stub vgs.persistVolumeGroup(msg.arg1); break; + case MSG_SET_INPUT_GAIN_INDEX: + setInputGainIndexInt((AudioDeviceAttributes) msg.obj, msg.arg1); + break; + + case MSG_PERSIST_INPUT_GAIN_INDEX: + persistInputGainIndex((AudioDeviceAttributes) msg.obj, msg.arg1); + break; + case MSG_PERSIST_RINGER_MODE: // note that the value persisted is the current ringer mode, not the // value of ringer mode as of the time the request was made to persist @@ -10147,6 +10268,11 @@ public class AudioService extends IAudioService.Stub mAudioEventWakeLock.release(); break; + case MSG_INIT_INPUT_GAINS: + onInitInputGains(); + mAudioEventWakeLock.release(); + break; + case MSG_INIT_ADI_DEVICE_STATES: onInitAdiDeviceStates(); mAudioEventWakeLock.release(); diff --git a/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java new file mode 100644 index 000000000000..d83dca629d74 --- /dev/null +++ b/services/core/java/com/android/server/audio/InputDeviceVolumeHelper.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2014 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.audio; + +import static android.media.AudioManager.GET_DEVICES_INPUTS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentResolver; +import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.os.UserHandle; +import android.util.IntArray; +import android.util.SparseIntArray; + +import java.util.HashSet; +import java.util.Set; + +/** Maintains the current state of input gains. */ +/*package*/ class InputDeviceVolumeHelper { + private static final String TAG = "InputDeviceVolumeHelper"; + + // TODO(b/364923030): retrieve these constants from AudioPolicyManager. + private final int INDEX_MIN = 0; + private final int INDEX_MAX = 100; + private final int INDEX_DEFAULT = 50; + + private final SettingsAdapter mSettings; + private final ContentResolver mContentResolver; + private final Object mSettingsLock; + private final String mInputGainIndexSettingsName; + + // A map between device internal type (e.g. AudioSystem.DEVICE_IN_BUILTIN_MIC) to its input gain + // index. + private final SparseIntArray mInputGainIndexMap; + private final Set<Integer> mSupportedDeviceTypes; + + InputDeviceVolumeHelper( + SettingsAdapter settings, + ContentResolver contentResolver, + Object settingsLock, + String settingsName) { + mSettings = settings; + mContentResolver = contentResolver; + mSettingsLock = settingsLock; + mInputGainIndexSettingsName = settingsName; + + IntArray internalDeviceTypes = new IntArray(); + int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes); + mInputGainIndexMap = + new SparseIntArray( + status == AudioManager.SUCCESS + ? internalDeviceTypes.size() + : AudioSystem.DEVICE_IN_ALL_SET.size()); + + if (status == AudioManager.SUCCESS) { + Set<Integer> supportedDeviceTypes = new HashSet<>(); + for (int i = 0; i < internalDeviceTypes.size(); i++) { + supportedDeviceTypes.add(internalDeviceTypes.get(i)); + } + mSupportedDeviceTypes = supportedDeviceTypes; + } else { + mSupportedDeviceTypes = AudioSystem.DEVICE_IN_ALL_SET; + } + + readSettings(); + } + + public void readSettings() { + synchronized (InputDeviceVolumeHelper.class) { + for (int inputDeviceType : mSupportedDeviceTypes) { + // Retrieve current input gain for device. If no input gain stored for current + // device, use default input gain. + int index; + if (!hasValidSettingsName()) { + index = INDEX_DEFAULT; + } else { + String name = getSettingNameForDevice(inputDeviceType); + index = + mSettings.getSystemIntForUser( + mContentResolver, name, INDEX_DEFAULT, UserHandle.USER_CURRENT); + } + + mInputGainIndexMap.put(inputDeviceType, getValidIndex(index)); + } + } + } + + public boolean hasValidSettingsName() { + return mInputGainIndexSettingsName != null && !mInputGainIndexSettingsName.isEmpty(); + } + + public @Nullable String getSettingNameForDevice(int inputDeviceType) { + if (!hasValidSettingsName()) { + return null; + } + final String suffix = AudioSystem.getInputDeviceName(inputDeviceType); + if (suffix.isEmpty()) { + return mInputGainIndexSettingsName; + } + return mInputGainIndexSettingsName + "_" + suffix; + } + + private int getValidIndex(int index) { + if (index < INDEX_MIN) { + return INDEX_MIN; + } + if (index > INDEX_MAX) { + return INDEX_MAX; + } + return index; + } + + public int getInputGainIndex(@NonNull AudioDeviceAttributes ada) { + int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType()); + ensureValidInputDeviceType(inputDeviceType); + + synchronized (InputDeviceVolumeHelper.class) { + return mInputGainIndexMap.get(inputDeviceType, INDEX_DEFAULT); + } + } + + public int getMaxInputGainIndex() { + return INDEX_MAX; + } + + public int getMinInputGainIndex() { + return INDEX_MIN; + } + + public boolean isInputGainFixed(@NonNull AudioDeviceAttributes ada) { + int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType()); + ensureValidInputDeviceType(inputDeviceType); + + // For simplicity, all devices have non fixed input gain. This might change + // when more input devices are supported and some do not support input gain control. + return false; + } + + public boolean setInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) { + int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType()); + ensureValidInputDeviceType(inputDeviceType); + + int oldIndex; + synchronized (mSettingsLock) { + synchronized (InputDeviceVolumeHelper.class) { + oldIndex = getInputGainIndex(ada); + index = getValidIndex(index); + + if (oldIndex == index) { + return false; + } + + mInputGainIndexMap.put(inputDeviceType, index); + return true; + } + } + } + + public void persistInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) { + int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType()); + ensureValidInputDeviceType(inputDeviceType); + + if (hasValidSettingsName()) { + mSettings.putSystemIntForUser( + mContentResolver, + getSettingNameForDevice(inputDeviceType), + index, + UserHandle.USER_CURRENT); + } + } + + public boolean isValidInputDeviceType(int inputDeviceType) { + return mSupportedDeviceTypes.contains(inputDeviceType); + } + + private void ensureValidInputDeviceType(int inputDeviceType) { + if (!isValidInputDeviceType(inputDeviceType)) { + throw new IllegalArgumentException("Bad input device type " + inputDeviceType); + } + } +} diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 774041152d3e..448c42b71731 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -82,7 +82,8 @@ public class AutomaticBrightnessController { @IntDef(prefix = { "AUTO_BRIGHTNESS_MODE_" }, value = { AUTO_BRIGHTNESS_MODE_DEFAULT, AUTO_BRIGHTNESS_MODE_IDLE, - AUTO_BRIGHTNESS_MODE_DOZE + AUTO_BRIGHTNESS_MODE_DOZE, + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR }) @Retention(RetentionPolicy.SOURCE) public @interface AutomaticBrightnessMode{} @@ -90,6 +91,7 @@ public class AutomaticBrightnessController { public static final int AUTO_BRIGHTNESS_MODE_DEFAULT = 0; public static final int AUTO_BRIGHTNESS_MODE_IDLE = 1; public static final int AUTO_BRIGHTNESS_MODE_DOZE = 2; + public static final int AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR = 3; public static final int AUTO_BRIGHTNESS_MODE_MAX = AUTO_BRIGHTNESS_MODE_DOZE; // How long the current sensor reading is assumed to be valid beyond the current time. diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 6a019f3d024c..570d5d0fc134 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -18,6 +18,7 @@ package com.android.server.display; import static android.text.TextUtils.formatSimple; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -114,7 +115,7 @@ public abstract class BrightnessMappingStrategy { luxLevels = getLuxLevels(context.getResources().getIntArray( com.android.internal.R.array.config_autoBrightnessLevelsIdle)); } - case AUTO_BRIGHTNESS_MODE_DOZE -> { + case AUTO_BRIGHTNESS_MODE_DOZE, AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR -> { luxLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset); brightnessLevels = displayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset); diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java index 1d68ee54d96c..83b0801ce87f 100644 --- a/services/core/java/com/android/server/display/BrightnessRangeController.java +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -67,6 +67,10 @@ class BrightnessRangeController { mNormalBrightnessModeController.resetNbmData( displayDeviceConfig.getLuxThrottlingData()); } + if (flags.useNewHdrBrightnessModifier()) { + // HDR boost is handled by HdrBrightnessModifier and should be disabled in HbmController + mHbmController.disableHdrBoost(); + } updateHdrClamper(info, displayToken, displayDeviceConfig); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 42a62f098b6a..a9ed0aaf324b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -19,6 +19,7 @@ package com.android.server.display; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -506,6 +507,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The time of inactivity after which the stylus can be assumed to be no longer in use. private long mIdleStylusTimeoutMillisConfig = 0; + // Whether wear bedtime mode is enabled in the settings. + private boolean mIsWearBedtimeModeEnabled; + /** * Creates the display power controller. */ @@ -562,6 +566,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessTracker = brightnessTracker; mOnBrightnessChangeRunnable = onBrightnessChangeRunnable; + mIsWearBedtimeModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.Wearable.BEDTIME_MODE, /* def= */ 0) == 1; + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE), + false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL); + final Resources resources = context.getResources(); // DOZE AND DIM SETTINGS @@ -1086,6 +1096,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessMappers.put(AUTO_BRIGHTNESS_MODE_DOZE, dozeModeBrightnessMapper); } + if (mFlags.areAutoBrightnessModesEnabled() + && mFlags.isAutoBrightnessModeBedtimeWearEnabled()) { + BrightnessMappingStrategy bedtimeBrightnessMapper = + BrightnessMappingStrategy.create(context, mDisplayDeviceConfig, + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, mDisplayWhiteBalanceController); + if (bedtimeBrightnessMapper != null) { + brightnessMappers.put(AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, bedtimeBrightnessMapper); + } + } + float userLux = BrightnessMappingStrategy.INVALID_LUX; float userNits = BrightnessMappingStrategy.INVALID_NITS; if (mAutomaticBrightnessController != null) { @@ -1155,8 +1175,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController, mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(), - mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits, - mBrightnessClamperController, mFlags); + mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits, mFlags); mDisplayBrightnessController.setUpAutoBrightness( mAutomaticBrightnessController, mSensorManager, mDisplayDeviceConfig, mHandler, defaultModeBrightnessMapper, mIsEnabled, mLeadDisplayId); @@ -1375,9 +1394,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean useDozeBrightness = mFlags.isNormalBrightnessForDozeParameterEnabled() ? (!mPowerRequest.useNormalBrightnessForDoze && mPowerRequest.policy == POLICY_DOZE) || Display.isDozeState(state) : Display.isDozeState(state); - - DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController - .updateBrightness(mPowerRequest, state, mDisplayOffloadSession); + DisplayBrightnessState displayBrightnessState = + mDisplayBrightnessController.updateBrightness( + mPowerRequest, + state, + mDisplayOffloadSession, + mIsWearBedtimeModeEnabled); float brightnessState = displayBrightnessState.getBrightness(); float rawBrightnessState = displayBrightnessState.getBrightness(); mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); @@ -1418,16 +1440,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && !mAutomaticBrightnessController.isInIdleMode()) { // Set sendUpdate to false, we're already in updatePowerState() so there's no need // to trigger it again - mAutomaticBrightnessController.switchMode(useDozeBrightness - ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, - /* sendUpdate= */ false); + if (useDozeBrightness) { + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, + /* sendUpdate= */ false); + } else if (mFlags.isAutoBrightnessModeBedtimeWearEnabled() + && mIsWearBedtimeModeEnabled) { + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + /* sendUpdate= */ false); + } else { + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, + /* sendUpdate= */ false); + } } mAutomaticBrightnessStrategy.setAutoBrightnessState(state, allowAutoBrightnessWhileDozing, mBrightnessReasonTemp.getReason(), mPowerRequest.policy, mPowerRequest.useNormalBrightnessForDoze, mDisplayBrightnessController.getLastUserSetScreenBrightness(), - userSetBrightnessChanged); + userSetBrightnessChanged, mIsWearBedtimeModeEnabled); // If the brightness is already set then it's been overridden by something other than // the user, or is a temporary adjustment. @@ -1503,7 +1533,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // use the current brightness setting scaled by the doze scale factor rawBrightnessState = getDozeBrightnessForOffload(); brightnessState = clampScreenBrightness(rawBrightnessState); - updateScreenBrightnessSetting = false; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_MANUAL); mTempBrightnessEvent.setFlags( mTempBrightnessEvent.getFlags() | BrightnessEvent.FLAG_DOZE_SCALE); @@ -1513,6 +1542,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessState = clampScreenBrightness(rawBrightnessState); mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT); } + updateScreenBrightnessSetting = false; } if (!mFlags.isRefactorDisplayPowerControllerEnabled()) { @@ -3161,6 +3191,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + autoBrightnessPresetToString(preset)); setUpAutoBrightness(mContext, mHandler); sendUpdatePowerState(); + } else if (uri.equals( + Settings.Global.getUriFor(Settings.Global.Wearable.BEDTIME_MODE))) { + mIsWearBedtimeModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.Wearable.BEDTIME_MODE, /* def= */ 0) == 1; + Slog.i(mTag, "Update for bedtime mode. Enable: " + mIsWearBedtimeModeEnabled); + sendUpdatePowerState(); } else { handleSettingsChange(); } @@ -3264,7 +3300,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call BrightnessRangeController brightnessModeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 135cab6d0614..6be0c123d262 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -38,6 +38,7 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.FrameworkStatsLog; import com.android.server.display.DisplayManagerService.Clock; import com.android.server.display.config.HighBrightnessModeData; +import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.utils.DebugUtils; import java.io.PrintWriter; @@ -119,6 +120,14 @@ class HighBrightnessModeController { @Nullable private HighBrightnessModeMetadata mHighBrightnessModeMetadata; + /** + * If {@link DisplayManagerFlags#useNewHdrBrightnessModifier()} is ON, hdr boost is handled by + * {@link com.android.server.display.brightness.clamper.HdrBrightnessModifier} and should be + * disabled in this class. After flag is cleaned up, this field together with HDR handling + * should be cleaned up from this class. + */ + private boolean mHdrBoostDisabled = false; + HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg, @@ -323,6 +332,7 @@ class HighBrightnessModeController { pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode); pw.println(" width*height=" + mWidth + "*" + mHeight); + pw.println(" mHdrBoostDisabled=" + mHdrBoostDisabled); if (mHighBrightnessModeMetadata != null) { pw.println(" mRunningStartTimeMillis=" @@ -373,6 +383,11 @@ class HighBrightnessModeController { return mHbmData != null && mHighBrightnessModeMetadata != null; } + void disableHdrBoost() { + mHdrBoostDisabled = true; + unregisterHdrListener(); + } + private long calculateRemainingTime(long currentTime) { if (!deviceSupportsHbm()) { return 0; @@ -583,6 +598,9 @@ class HighBrightnessModeController { } private void registerHdrListener(IBinder displayToken) { + if (mHdrBoostDisabled) { + return; + } if (mRegisteredDisplayToken == displayToken) { return; } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 4bd980822e46..5b12dfb951d5 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -150,12 +150,13 @@ public final class DisplayBrightnessController { public DisplayBrightnessState updateBrightness( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, - DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession, + boolean isBedtimeModeWearEnabled) { DisplayBrightnessState state; synchronized (mLock) { mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy( constructStrategySelectionRequest(displayPowerRequest, targetDisplayState, - displayOffloadSession)); + displayOffloadSession, isBedtimeModeWearEnabled)); state = mDisplayBrightnessStrategy .updateBrightness(constructStrategyExecutionRequest(displayPowerRequest)); } @@ -629,7 +630,8 @@ public final class DisplayBrightnessController { private StrategySelectionRequest constructStrategySelectionRequest( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, - DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession, + boolean isBedtimeModeEnabled) { boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); float lastUserSetScreenBrightness; synchronized (mLock) { @@ -637,7 +639,7 @@ public final class DisplayBrightnessController { } return new StrategySelectionRequest(displayPowerRequest, targetDisplayState, lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession, - mIsStylusBeingUsed); + mIsStylusBeingUsed, isBedtimeModeEnabled); } private StrategyExecutionRequest constructStrategyExecutionRequest( diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index ded7447c5fbc..60c1b8ff4db9 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -305,7 +305,8 @@ public class DisplayBrightnessStrategySelector { strategySelectionRequest.getDisplayPowerRequest().policy, strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze, strategySelectionRequest.getLastUserSetScreenBrightness(), - strategySelectionRequest.isUserSetBrightnessChanged()); + strategySelectionRequest.isUserSetBrightnessChanged(), + strategySelectionRequest.isWearBedtimeModeEnabled()); return !strategySelectionRequest.isStylusBeingUsed() && mAutomaticBrightnessStrategy1.isAutoBrightnessValid(); } @@ -320,7 +321,8 @@ public class DisplayBrightnessStrategySelector { strategySelectionRequest.getLastUserSetScreenBrightness(), strategySelectionRequest.isUserSetBrightnessChanged(), mAllowAutoBrightnessWhileDozing, - getAutomaticBrightnessStrategy().shouldUseAutoBrightness()); + getAutomaticBrightnessStrategy().shouldUseAutoBrightness(), + strategySelectionRequest.isWearBedtimeModeEnabled()); } private void postProcess(StrategySelectionNotifyRequest strategySelectionNotifyRequest) { diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java index bfa90e297059..c2bec5ffe052 100644 --- a/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategySelectionNotifyRequest.java @@ -47,13 +47,15 @@ public final class StrategySelectionNotifyRequest { private final boolean mAllowAutoBrightnessWhileDozingConfig; // True if the auto brightness is enabled in the settings private final boolean mIsAutoBrightnessEnabled; + // True if wear bedtime mode is enabled in the settings. + private final boolean mIsBedtimeModeWearEnabled; public StrategySelectionNotifyRequest( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, DisplayBrightnessStrategy displayBrightnessStrategy, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged, boolean allowAutoBrightnessWhileDozingConfig, - boolean isAutoBrightnessEnabled) { + boolean isAutoBrightnessEnabled, boolean isBedtimeModeWearEnabled) { mDisplayPowerRequest = displayPowerRequest; mTargetDisplayState = targetDisplayState; mSelectedDisplayBrightnessStrategy = displayBrightnessStrategy; @@ -61,6 +63,7 @@ public final class StrategySelectionNotifyRequest { mUserSetBrightnessChanged = userSetBrightnessChanged; mAllowAutoBrightnessWhileDozingConfig = allowAutoBrightnessWhileDozingConfig; mIsAutoBrightnessEnabled = isAutoBrightnessEnabled; + mIsBedtimeModeWearEnabled = isBedtimeModeWearEnabled; } public DisplayBrightnessStrategy getSelectedDisplayBrightnessStrategy() { @@ -81,7 +84,8 @@ public final class StrategySelectionNotifyRequest { && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness() && mAllowAutoBrightnessWhileDozingConfig == other.isAllowAutoBrightnessWhileDozingConfig() - && mIsAutoBrightnessEnabled == other.isAutoBrightnessEnabled(); + && mIsAutoBrightnessEnabled == other.isAutoBrightnessEnabled() + && mIsBedtimeModeWearEnabled == other.isBedtimeModeWearEnabled(); } @Override @@ -99,6 +103,10 @@ public final class StrategySelectionNotifyRequest { return mUserSetBrightnessChanged; } + public boolean isBedtimeModeWearEnabled() { + return mIsBedtimeModeWearEnabled; + } + public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { return mDisplayPowerRequest; } @@ -126,6 +134,7 @@ public final class StrategySelectionNotifyRequest { + " mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness + " mUserSetBrightnessChanged=" + mUserSetBrightnessChanged + " mAllowAutoBrightnessWhileDozingConfig=" + mAllowAutoBrightnessWhileDozingConfig - + " mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled; + + " mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled + + " mIsBedtimeModeWearEnabled" + mIsBedtimeModeWearEnabled; } } diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java index 5c1f03d877a6..2077c9dfb79c 100644 --- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java @@ -42,17 +42,21 @@ public final class StrategySelectionRequest { private boolean mIsStylusBeingUsed; + private boolean mIsWearBedtimeModeEnabled; + public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged, DisplayManagerInternal.DisplayOffloadSession displayOffloadSession, - boolean isStylusBeingUsed) { + boolean isStylusBeingUsed, + boolean isWearBedtimeModeEnabled) { mDisplayPowerRequest = displayPowerRequest; mTargetDisplayState = targetDisplayState; mLastUserSetScreenBrightness = lastUserSetScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; mDisplayOffloadSession = displayOffloadSession; mIsStylusBeingUsed = isStylusBeingUsed; + mIsWearBedtimeModeEnabled = isWearBedtimeModeEnabled; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -72,6 +76,10 @@ public final class StrategySelectionRequest { return mUserSetBrightnessChanged; } + public boolean isWearBedtimeModeEnabled() { + return mIsWearBedtimeModeEnabled; + } + public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSession() { return mDisplayOffloadSession; } @@ -91,7 +99,8 @@ public final class StrategySelectionRequest { && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness() && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()) - && mIsStylusBeingUsed == other.isStylusBeingUsed(); + && mIsStylusBeingUsed == other.isStylusBeingUsed() + && mIsWearBedtimeModeEnabled == other.isWearBedtimeModeEnabled(); } @Override diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 2fdec78bbbda..ff73693d803b 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -17,6 +17,7 @@ package com.android.server.display.brightness.strategy; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; @@ -132,10 +133,11 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 public void setAutoBrightnessState(int targetDisplayState, boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy, boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness, - boolean userSetBrightnessChanged) { + boolean userSetBrightnessChanged, boolean isBedtimeModeWearEnabled) { // We are still in the process of updating the power state, so there's no need to trigger // an update again - switchMode(targetDisplayState, useNormalBrightnessForDoze, policy, /* sendUpdate= */ false); + switchMode(targetDisplayState, useNormalBrightnessForDoze, policy, isBedtimeModeWearEnabled, + /* sendUpdate= */ false); // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness // should only be enabled if the config allows it @@ -349,7 +351,8 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 strategySelectionNotifyRequest.getDisplayPowerRequest() .useNormalBrightnessForDoze, strategySelectionNotifyRequest.getLastUserSetScreenBrightness(), - strategySelectionNotifyRequest.isUserSetBrightnessChanged()); + strategySelectionNotifyRequest.isUserSetBrightnessChanged(), + strategySelectionNotifyRequest.isBedtimeModeWearEnabled()); } mIsConfigured = false; } @@ -505,18 +508,33 @@ public class AutomaticBrightnessStrategy extends AutomaticBrightnessStrategy2 } private void switchMode(int state, boolean useNormalBrightnessForDoze, int policy, - boolean sendUpdate) { - if (mDisplayManagerFlags.areAutoBrightnessModesEnabled() - && mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode()) { - - final boolean shouldUseDozeMode = - mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled() - ? (!useNormalBrightnessForDoze && policy == POLICY_DOZE) - || Display.isDozeState(state) : Display.isDozeState(state); - mAutomaticBrightnessController.switchMode(shouldUseDozeMode - ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate); + boolean isWearBedtimeModeEnabled, boolean sendUpdate) { + if (!mDisplayManagerFlags.areAutoBrightnessModesEnabled() + || mAutomaticBrightnessController == null + || mAutomaticBrightnessController.isInIdleMode()) { + return; + } + + final boolean shouldUseBedtimeMode = + mDisplayManagerFlags.isAutoBrightnessModeBedtimeWearEnabled() + && isWearBedtimeModeEnabled; + if (shouldUseBedtimeMode) { + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + sendUpdate); + return; } + + final boolean shouldUseDozeMode = + mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled() + ? (!useNormalBrightnessForDoze && policy == POLICY_DOZE) + || Display.isDozeState(state) + : Display.isDozeState(state); + if (shouldUseDozeMode) { + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, sendUpdate); + return; + } + + mAutomaticBrightnessController.switchMode(AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate); } /** diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java index 5e79565f7c54..6e2e7e704997 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java @@ -109,7 +109,7 @@ public class AutomaticBrightnessStrategy2 { public void setAutoBrightnessState(int targetDisplayState, boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy, boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness, - boolean userSetBrightnessChanged) { + boolean userSetBrightnessChanged, boolean isBedtimeModeEnabled) { // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness // should only be enabled if the config allows it final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java index e0bdda511df3..458438c3f0b0 100644 --- a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java +++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java @@ -16,6 +16,7 @@ package com.android.server.display.config; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -247,6 +248,9 @@ public class DisplayBrightnessMappingConfig { case AUTO_BRIGHTNESS_MODE_DOZE -> { return AutoBrightnessModeName.doze.getRawName(); } + case AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR -> { + return AutoBrightnessModeName.bedtime_wear.getRawName(); + } default -> throw new IllegalArgumentException("Unknown auto-brightness mode: " + mode); } } diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java index adcda0a63152..35b3673fdf77 100644 --- a/services/core/java/com/android/server/incident/PendingReports.java +++ b/services/core/java/com/android/server/incident/PendingReports.java @@ -304,16 +304,16 @@ class PendingReports { denyReportBeforeAddingRec(listener, callingPackage); return; } + AttributionSource attributionSource = + new AttributionSource.Builder(callingUid) + .setPackageName(callingPackage) + .build(); // Only with userdebug/eng build: it could check capture consentless bugreport permission // and approve the report when it's granted. boolean captureConsentlessBugreportOnUserdebugBuildGranted = false; if ((Build.IS_USERDEBUG || Build.IS_ENG) && (flags & IncidentManager.FLAG_ALLOW_CONSENTLESS_BUGREPORT) != 0) { - AttributionSource attributionSource = - new AttributionSource.Builder(callingUid) - .setPackageName(callingPackage) - .build(); captureConsentlessBugreportOnUserdebugBuildGranted = mPermissionManager.checkPermissionForDataDelivery( Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD, @@ -321,12 +321,32 @@ class PendingReports { /* message= */ null) == PERMISSION_GRANTED; } - if (captureConsentlessBugreportOnUserdebugBuildGranted) { + + // Allow system apps to skip the consent dialog and use their in-built consent mechanism + // instead. + boolean captureConsentlessBugreportDelegatedConsentGranted = false; + if ((flags & IncidentManager.FLAG_ALLOW_CONSENTLESS_BUGREPORT) != 0) { + captureConsentlessBugreportDelegatedConsentGranted = + mPermissionManager.checkPermissionForDataDelivery( + Manifest.permission + .CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT, + attributionSource, + /* message= */ null) + == PERMISSION_GRANTED; + } + + if (captureConsentlessBugreportOnUserdebugBuildGranted + || captureConsentlessBugreportDelegatedConsentGranted) { try { PendingReportRec rec = new PendingReportRec( callingPackage, receiverClass, reportId, flags, listener); - Log.d(TAG, "approving consentless report: " + rec.getUri()); + if (captureConsentlessBugreportOnUserdebugBuildGranted) { + Log.d(TAG, "approving consentless report: " + rec.getUri()); + } + if (captureConsentlessBugreportDelegatedConsentGranted) { + Log.d(TAG, "delegating consent for report: " + rec.getUri()); + } listener.onReportApproved(); return; } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/incident/TEST_MAPPING b/services/core/java/com/android/server/incident/TEST_MAPPING new file mode 100644 index 000000000000..4f789dbba2b8 --- /dev/null +++ b/services/core/java/com/android/server/incident/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "postsubmit": [ + { + "name": "CtsRootBugreportTestCases" + }, + { + "name": "BugreportManagerTestCases" + } + ] +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index c888eef7f5df..1c5bd59fa386 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -127,6 +127,13 @@ public abstract class InputManagerInternal { */ public abstract void notifyInputMethodConnectionActive(boolean connectionIsActive); + /** + * Notify user id changes to input. + * + * TODO(b/362473586): Cleanup after input shifts to Lifecycle with user change callbacks + */ + public abstract void setCurrentUser(@UserIdInt int newUserId); + /** Callback interface for notifications relating to the lid switch. */ public interface LidSwitchCallback { /** @@ -270,4 +277,18 @@ public abstract class InputManagerInternal { * @param scaleFactor the new scale factor to be applied for pointer icons. */ public abstract void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor); + + /** + * Set whether the given input device can wake up the kernel from sleep + * when it generates input events. By default, usually only internal (built-in) + * input devices can wake the kernel from sleep. For an external input device + * that supports remote wakeup to be able to wake the kernel, this must be called + * after each time the device is connected/added. + * + * @param deviceId the device ID of the input device. + * @param enabled When true, device will be configured to wake up kernel. + * + * @return true if setting power wakeup was successful. + */ + public abstract boolean setKernelWakeEnabled(int deviceId, boolean enabled); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 98e5319cde30..a421d044507a 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -175,6 +175,7 @@ public class InputManagerService extends IInputManager.Stub private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1; private static final int MSG_RELOAD_DEVICE_ALIASES = 2; private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3; + private static final int MSG_CURRENT_USER_CHANGED = 4; private static final int DEFAULT_VIBRATION_MAGNITUDE = 192; private static final AdditionalDisplayInputProperties @@ -184,6 +185,8 @@ public class InputManagerService extends IInputManager.Stub private final Context mContext; private final InputManagerHandler mHandler; + @UserIdInt + private int mCurrentUserId = UserHandle.USER_SYSTEM; private DisplayManagerInternal mDisplayManagerInternal; private WindowManagerInternal mWindowManagerInternal; @@ -2982,6 +2985,10 @@ public class InputManagerService extends IInputManager.Stub mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid()); } + private void handleCurrentUserChanged(@UserIdInt int userId) { + mCurrentUserId = userId; + } + /** * Callback interface implemented by the Window Manager. */ @@ -3150,6 +3157,9 @@ public class InputManagerService extends IInputManager.Stub boolean inTabletMode = (boolean) args.arg1; deliverTabletModeChanged(whenNanos, inTabletMode); break; + case MSG_CURRENT_USER_CHANGED: + handleCurrentUserChanged((int) msg.obj); + break; } } } @@ -3511,6 +3521,16 @@ public class InputManagerService extends IInputManager.Stub public void setAccessibilityPointerIconScaleFactor(int displayId, float scaleFactor) { InputManagerService.this.setAccessibilityPointerIconScaleFactor(displayId, scaleFactor); } + + @Override + public void setCurrentUser(@UserIdInt int newUserId) { + mHandler.obtainMessage(MSG_CURRENT_USER_CHANGED, newUserId).sendToTarget(); + } + + @Override + public boolean setKernelWakeEnabled(int deviceId, boolean enabled) { + return mNative.setKernelWakeEnabled(deviceId, enabled); + } } @Override diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index 2f5236f51c48..4d93e6570a79 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE; +import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; @@ -577,6 +578,17 @@ final class KeyGestureController { focusedToken, /* flags = */0); } break; + case KeyEvent.KEYCODE_T: + if (keyboardA11yShortcutControl()) { + if (firstDown && event.isMetaPressed() && event.isAltPressed()) { + return handleKeyGesture(deviceId, new int[]{keyCode}, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0); + } + } + break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed() @@ -1000,16 +1012,16 @@ final class KeyGestureController { if (device == null) { return; } + KeyGestureEvent keyGestureEvent = new KeyGestureEvent(event); if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) { KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event.keycodes, - event.modifierState, - KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType)); + event.modifierState, keyGestureEvent.getLogEvent()); } notifyAllListeners(event); while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) { mLastHandledEvents.removeFirst(); } - mLastHandledEvents.addLast(new KeyGestureEvent(event)); + mLastHandledEvents.addLast(keyGestureEvent); } @MainThread diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 21e8bccd2883..283fdea92b63 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -287,6 +287,17 @@ interface NativeInputManagerService { */ int getLastUsedInputDeviceId(); + /** + * Set whether the given input device can wake up the kernel from sleep + * when it generates input events. By default, usually only internal (built-in) + * input devices can wake the kernel from sleep. For an external input device + * that supports remote wakeup to be able to wake the kernel, this must be called + * after each time the device is connected/added. + * + * Returns true if setting power wakeup was successful. + */ + boolean setKernelWakeEnabled(int deviceId, boolean enabled); + /** The native implementation of InputManagerService methods. */ class NativeImpl implements NativeInputManagerService { /** Pointer to native input manager service object, used by native code. */ @@ -573,5 +584,8 @@ interface NativeInputManagerService { @Override public native int getLastUsedInputDeviceId(); + + @Override + public native boolean setKernelWakeEnabled(int deviceId, boolean enabled); } } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 509fa3e1c9ba..bb4ae96da53b 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -74,7 +74,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private final Context mContext; private final Handler mHandler; private final PackageManagerInternal mPackageManagerInternal; - private final IntegrityFileManager mIntegrityFileManager; /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ public static AppIntegrityManagerServiceImpl create(Context context) { @@ -84,7 +83,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return new AppIntegrityManagerServiceImpl( context, LocalServices.getService(PackageManagerInternal.class), - IntegrityFileManager.getInstance(), handlerThread.getThreadHandler()); } @@ -92,11 +90,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { AppIntegrityManagerServiceImpl( Context context, PackageManagerInternal packageManagerInternal, - IntegrityFileManager integrityFileManager, Handler handler) { mContext = context; mPackageManagerInternal = packageManagerInternal; - mIntegrityFileManager = integrityFileManager; mHandler = handler; IntentFilter integrityVerificationFilter = new IntentFilter(); @@ -144,39 +140,23 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { @Override @BinderThread public String getCurrentRuleSetVersion() { - getCallerPackageNameOrThrow(Binder.getCallingUid()); - - RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata(); - return (ruleMetadata != null && ruleMetadata.getVersion() != null) - ? ruleMetadata.getVersion() - : ""; + return ""; } @Override @BinderThread public String getCurrentRuleSetProvider() { - getCallerPackageNameOrThrow(Binder.getCallingUid()); - - RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata(); - return (ruleMetadata != null && ruleMetadata.getRuleProvider() != null) - ? ruleMetadata.getRuleProvider() - : ""; + return ""; } @Override public ParceledListSlice<Rule> getCurrentRules() { - List<Rule> rules = Collections.emptyList(); - try { - rules = mIntegrityFileManager.readRules(/* appInstallMetadata= */ null); - } catch (Exception e) { - Slog.e(TAG, "Error getting current rules", e); - } - return new ParceledListSlice<>(rules); + return new ParceledListSlice<>(Collections.emptyList()); } @Override public List<String> getWhitelistedRuleProviders() { - return getAllowedRuleProviderSystemApps(); + return Collections.emptyList(); } private void handleIntegrityVerification(Intent intent) { @@ -184,75 +164,4 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { mPackageManagerInternal.setIntegrityVerificationResult( verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); } - - private String getCallerPackageNameOrThrow(int callingUid) { - String callerPackageName = getCallingRulePusherPackageName(callingUid); - if (callerPackageName == null) { - throw new SecurityException( - "Only system packages specified in config_integrityRuleProviderPackages are " - + "allowed to call this method."); - } - return callerPackageName; - } - - private String getCallingRulePusherPackageName(int callingUid) { - // Obtain the system apps that are allowlisted in config_integrityRuleProviderPackages. - List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); - if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i( - TAG, - String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); - } - - // Identify the package names in the caller list. - List<String> callingPackageNames = getPackageListForUid(callingUid); - - // Find the intersection between the allowed and calling packages. Ideally, we will have - // at most one package name here. But if we have more, it is fine. - List<String> allowedCallingPackages = new ArrayList<>(); - for (String packageName : callingPackageNames) { - if (allowedRuleProviders.contains(packageName)) { - allowedCallingPackages.add(packageName); - } - } - - return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0); - } - - private List<String> getAllowedRuleProviderSystemApps() { - List<String> integrityRuleProviders = - Arrays.asList( - mContext.getResources() - .getStringArray(R.array.config_integrityRuleProviderPackages)); - - // Filter out the rule provider packages that are not system apps. - List<String> systemAppRuleProviders = new ArrayList<>(); - for (String ruleProvider : integrityRuleProviders) { - if (isSystemApp(ruleProvider)) { - systemAppRuleProviders.add(ruleProvider); - } - } - return systemAppRuleProviders; - } - - private boolean isSystemApp(String packageName) { - try { - PackageInfo existingPackageInfo = - mContext.getPackageManager().getPackageInfo(packageName, /* flags= */ 0); - return existingPackageInfo.applicationInfo != null - && existingPackageInfo.applicationInfo.isSystemApp(); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - - private List<String> getPackageListForUid(int uid) { - try { - return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid)); - } catch (NullPointerException e) { - Slog.w(TAG, String.format("No packages were found for uid: %d", uid)); - return List.of(); - } - } } diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java deleted file mode 100644 index 7f0231e72373..000000000000 --- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity; - -import android.annotation.Nullable; -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.Rule; -import android.os.Environment; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.integrity.model.RuleMetadata; -import com.android.server.integrity.parser.RandomAccessObject; -import com.android.server.integrity.parser.RuleBinaryParser; -import com.android.server.integrity.parser.RuleIndexRange; -import com.android.server.integrity.parser.RuleIndexingController; -import com.android.server.integrity.parser.RuleMetadataParser; -import com.android.server.integrity.parser.RuleParseException; -import com.android.server.integrity.parser.RuleParser; -import com.android.server.integrity.serializer.RuleBinarySerializer; -import com.android.server.integrity.serializer.RuleMetadataSerializer; -import com.android.server.integrity.serializer.RuleSerializeException; -import com.android.server.integrity.serializer.RuleSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -/** Abstraction over the underlying storage of rules and other metadata. */ -public class IntegrityFileManager { - private static final String TAG = "IntegrityFileManager"; - - private static final String METADATA_FILE = "metadata"; - private static final String RULES_FILE = "rules"; - private static final String INDEXING_FILE = "indexing"; - private static final Object RULES_LOCK = new Object(); - - private static IntegrityFileManager sInstance = null; - - private final RuleParser mRuleParser; - private final RuleSerializer mRuleSerializer; - - private final File mDataDir; - // mRulesDir contains data of the actual rules currently stored. - private final File mRulesDir; - // mStagingDir is used to store the temporary rules / metadata during updating, since we want to - // update rules atomically. - private final File mStagingDir; - - @Nullable private RuleMetadata mRuleMetadataCache; - @Nullable private RuleIndexingController mRuleIndexingController; - - /** Get the singleton instance of this class. */ - public static synchronized IntegrityFileManager getInstance() { - if (sInstance == null) { - sInstance = new IntegrityFileManager(); - } - return sInstance; - } - - private IntegrityFileManager() { - this( - new RuleBinaryParser(), - new RuleBinarySerializer(), - Environment.getDataSystemDirectory()); - } - - @VisibleForTesting - IntegrityFileManager(RuleParser ruleParser, RuleSerializer ruleSerializer, File dataDir) { - mRuleParser = ruleParser; - mRuleSerializer = ruleSerializer; - mDataDir = dataDir; - - mRulesDir = new File(dataDir, "integrity_rules"); - mStagingDir = new File(dataDir, "integrity_staging"); - - if (!mStagingDir.mkdirs() || !mRulesDir.mkdirs()) { - Slog.e(TAG, "Error creating staging and rules directory"); - // TODO: maybe throw an exception? - } - - File metadataFile = new File(mRulesDir, METADATA_FILE); - if (metadataFile.exists()) { - try (FileInputStream inputStream = new FileInputStream(metadataFile)) { - mRuleMetadataCache = RuleMetadataParser.parse(inputStream); - } catch (Exception e) { - Slog.e(TAG, "Error reading metadata file.", e); - } - } - - updateRuleIndexingController(); - } - - /** - * Returns if the rules have been initialized. - * - * <p>Used to fail early if there are no rules (so we don't need to parse the apk at all). - */ - public boolean initialized() { - return new File(mRulesDir, RULES_FILE).exists() - && new File(mRulesDir, METADATA_FILE).exists() - && new File(mRulesDir, INDEXING_FILE).exists(); - } - - /** Write rules to persistent storage. */ - public void writeRules(String version, String ruleProvider, List<Rule> rules) - throws IOException, RuleSerializeException { - try { - writeMetadata(mStagingDir, ruleProvider, version); - } catch (IOException e) { - Slog.e(TAG, "Error writing metadata.", e); - // We don't consider this fatal so we continue execution. - } - - try (FileOutputStream ruleFileOutputStream = - new FileOutputStream(new File(mStagingDir, RULES_FILE)); - FileOutputStream indexingFileOutputStream = - new FileOutputStream(new File(mStagingDir, INDEXING_FILE))) { - mRuleSerializer.serialize( - rules, Optional.empty(), ruleFileOutputStream, indexingFileOutputStream); - } - - switchStagingRulesDir(); - - // Update object holding the indexing information. - updateRuleIndexingController(); - } - - /** - * Read rules from persistent storage. - * - * @param appInstallMetadata information about the install used to select rules to read. If - * null, all rules will be read. - */ - public List<Rule> readRules(@Nullable AppInstallMetadata appInstallMetadata) - throws IOException, RuleParseException { - synchronized (RULES_LOCK) { - // Try to identify indexes from the index file. - List<RuleIndexRange> ruleReadingIndexes = Collections.emptyList(); - if (appInstallMetadata != null) { - try { - ruleReadingIndexes = - mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); - } catch (Exception e) { - Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e); - } - } - - // Read the rules based on the index information when available. - File ruleFile = new File(mRulesDir, RULES_FILE); - List<Rule> rules = - mRuleParser.parse(RandomAccessObject.ofFile(ruleFile), ruleReadingIndexes); - return rules; - } - } - - /** Read the metadata of the current rules in storage. */ - @Nullable - public RuleMetadata readMetadata() { - return mRuleMetadataCache; - } - - private void switchStagingRulesDir() throws IOException { - synchronized (RULES_LOCK) { - File tmpDir = new File(mDataDir, "temp"); - - if (!(mRulesDir.renameTo(tmpDir) - && mStagingDir.renameTo(mRulesDir) - && tmpDir.renameTo(mStagingDir))) { - throw new IOException("Error switching staging/rules directory"); - } - - for (File file : mStagingDir.listFiles()) { - file.delete(); - } - } - } - - private void updateRuleIndexingController() { - File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE); - if (ruleIndexingFile.exists()) { - try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) { - mRuleIndexingController = new RuleIndexingController(inputStream); - } catch (Exception e) { - Slog.e(TAG, "Error parsing the rule indexing file.", e); - } - } - } - - private void writeMetadata(File directory, String ruleProvider, String version) - throws IOException { - mRuleMetadataCache = new RuleMetadata(ruleProvider, version); - - File metadataFile = new File(directory, METADATA_FILE); - - try (FileOutputStream outputStream = new FileOutputStream(metadataFile)) { - RuleMetadataSerializer.serialize(mRuleMetadataCache, outputStream); - } - } -} diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index 598901e33305..556cc03b3abd 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -500,7 +500,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub checkNanoappPermsAsync(); } - if (!Flags.reliableMessageImplementation() || transactionCallback == null) { + if (transactionCallback == null) { try { result = mContextHubProxy.sendMessageToContextHub(mHostEndPointId, mAttachedContextHubInfo.getId(), message); @@ -671,10 +671,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub .putExtra(ContextHubManager.EXTRA_MESSAGE, message); Consumer<Byte> onFinishedCallback = (Byte error) -> sendMessageDeliveryStatusToContextHub(message.getMessageSequenceNumber(), error); - return sendPendingIntent(supplier, nanoAppId, - Flags.reliableMessageImplementation() && message.isReliable() - ? onFinishedCallback - : null); + return sendPendingIntent( + supplier, nanoAppId, message.isReliable() ? onFinishedCallback : null); } /** @@ -1284,10 +1282,6 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } private void sendMessageDeliveryStatusToContextHub(int messageSequenceNumber, byte errorCode) { - if (!Flags.reliableMessageImplementation()) { - return; - } - MessageDeliveryStatus status = new MessageDeliveryStatus(); status.messageSequenceNumber = messageSequenceNumber; status.errorCode = errorCode; diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java index 7285151ee598..0fdd0ae641df 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java @@ -236,7 +236,7 @@ import java.util.function.Consumer; } if (message.isBroadcastMessage()) { - if (Flags.reliableMessageImplementation() && message.isReliable()) { + if (message.isReliable()) { Log.e(TAG, "Received reliable broadcast message from " + message.getNanoAppId()); return ErrorCode.PERMANENT_ERROR; } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java index e1b1416169cd..53a02cd1b677 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java @@ -333,10 +333,6 @@ public class ContextHubEventLogger { */ public synchronized void logReliableMessageToNanoappStatus( int messageSequenceNumber, byte errorCode) { - if (!Flags.reliableMessage()) { - return; - } - for (NanoappMessageEvent event : mMessageToNanoappQueue) { if (event.message.isReliable() && event.message.getMessageSequenceNumber() diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index ed69f7ad32f6..acc8f6634f5c 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -228,9 +228,8 @@ public class ContextHubService extends IContextHubService.Stub { // Only process the message normally if not using test mode manager or if // the test mode manager call returned false as this indicates it did not // process the message. - boolean useTestModeManager = Flags.reliableMessageImplementation() - && Flags.reliableMessageTestModeBehavior() - && mIsTestModeEnabled.get(); + boolean useTestModeManager = + Flags.reliableMessageTestModeBehavior() && mIsTestModeEnabled.get(); if (!useTestModeManager || !mTestModeManager.handleNanoappMessage(() -> { handleClientMessageCallback(mContextHubId, hostEndpointId, @@ -945,8 +944,7 @@ public class ContextHubService extends IContextHubService.Stub { private void handleClientMessageCallback(int contextHubId, short hostEndpointId, NanoAppMessage message, List<String> nanoappPermissions, List<String> messagePermissions) { - if (!Flags.reliableMessageImplementation() - || !Flags.reliableMessageDuplicateDetectionService()) { + if (!Flags.reliableMessageDuplicateDetectionService()) { byte errorCode = mClientManager.onMessageFromNanoApp(contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions); if (message.isReliable() && errorCode != ErrorCode.OK) { @@ -1040,12 +1038,8 @@ public class ContextHubService extends IContextHubService.Stub { * @param messageSequenceNumber the message sequence number * @param errorCode the error code, one of the enum ErrorCode */ - private void sendMessageDeliveryStatusToContextHub(int contextHubId, - int messageSequenceNumber, byte errorCode) { - if (!Flags.reliableMessageImplementation()) { - return; - } - + private void sendMessageDeliveryStatusToContextHub( + int contextHubId, int messageSequenceNumber, byte errorCode) { MessageDeliveryStatus status = new MessageDeliveryStatus(); status.messageSequenceNumber = messageSequenceNumber; status.errorCode = errorCode; diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index a8ad41853d34..5e9277ac0faf 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -494,16 +494,11 @@ public abstract class IContextHubWrapper { } public void handleMessageDeliveryStatus( - char hostEndpointId, - MessageDeliveryStatus messageDeliveryStatus) { - if (Flags.reliableMessageImplementation()) { - mHandler.post(() -> { - mCallback.handleMessageDeliveryStatus(messageDeliveryStatus); - }); - } else { - Log.w(TAG, "handleMessageDeliveryStatus called when the " - + "reliableMessageImplementation flag is disabled"); - } + char hostEndpointId, MessageDeliveryStatus messageDeliveryStatus) { + mHandler.post( + () -> { + mCallback.handleMessageDeliveryStatus(messageDeliveryStatus); + }); } public byte[] getUuid() { @@ -682,9 +677,8 @@ public abstract class IContextHubWrapper { // Only process the message normally if not using test mode manager or if // the test mode manager call returned false as this indicates it did not // process the message. - boolean useTestModeManager = Flags.reliableMessageImplementation() - && Flags.reliableMessageTestModeBehavior() - && mIsTestModeEnabled.get(); + boolean useTestModeManager = + Flags.reliableMessageTestModeBehavior() && mIsTestModeEnabled.get(); if (!useTestModeManager || !mTestModeManager.sendMessageToContextHub( sendMessage, message)) { try { diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 542a29ae4172..4a9bf88aae33 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -2584,6 +2584,9 @@ public class LocationProviderManager extends registration -> { if (registration.getIdentity().getPackageName().equals( packageName)) { + if (D) { + Log.d(TAG, "package reset remove registration " + registration); + } registration.remove(); } diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index b0fa523da959..58c8450d714d 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -42,15 +42,16 @@ abstract class MediaRoute2Provider { final Object mLock = new Object(); Callback mCallback; - boolean mIsSystemRouteProvider; + public final boolean mIsSystemRouteProvider; private volatile MediaRoute2ProviderInfo mProviderInfo; @GuardedBy("mLock") final List<RoutingSessionInfo> mSessionInfos = new ArrayList<>(); - MediaRoute2Provider(@NonNull ComponentName componentName) { + MediaRoute2Provider(@NonNull ComponentName componentName, boolean isSystemRouteProvider) { mComponentName = Objects.requireNonNull(componentName, "Component name must not be null."); mUniqueId = componentName.flattenToShortString(); + mIsSystemRouteProvider = isSystemRouteProvider; } public void setCallback(Callback callback) { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 56b93e8ded82..db1e6b465ff8 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -96,7 +96,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider { @NonNull ComponentName componentName, boolean isSelfScanOnlyProvider, int userId) { - super(componentName); + super(componentName, /* isSystemRouteProvider= */ false); mContext = Objects.requireNonNull(context, "Context must not be null."); mRequestIdToSessionCreationRequest = new LongSparseArray<>(); mSessionOriginalIdToTransferRequest = new HashMap<>(); diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 8c6273ce959f..49897b9209cc 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -90,8 +90,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private volatile SessionCreationOrTransferRequest mPendingTransferRequest; SystemMediaRoute2Provider(Context context, UserHandle user, Looper looper) { - super(COMPONENT_NAME); - mIsSystemRouteProvider = true; + super(COMPONENT_NAME, /* isSystemRouteProvider= */ true); mContext = context; mUser = user; mHandler = new Handler(looper); diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index ecc4cf72a1c3..308f299a117b 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -16,7 +16,6 @@ package com.android.server.notification; -import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -68,7 +67,7 @@ public class EventConditionProvider extends SystemConditionProviderService { private final Handler mWorker; private final HandlerThread mThread; - @Nullable private UserHandle mCurrentUser; + private UserHandle mCurrentUser = UserHandle.SYSTEM; private boolean mConnected; private boolean mRegistered; private boolean mBootComplete; // don't hammer the calendar provider until boot completes. @@ -119,18 +118,19 @@ public class EventConditionProvider extends SystemConditionProviderService { @Override public void onReceive(Context context, Intent intent) { if (android.app.Flags.modesHsum()) { - if (mCurrentUser != null) { - // Possibly the intent signals a profile added on a different user, but it - // doesn't matter (except for a bit of wasted work here). We will reload - // trackers for that user when we switch. - reloadTrackers(mCurrentUser); - } + // Possibly the intent signals a profile added on a different user, but it + // doesn't matter (except for a bit of wasted work here). We will reload + // trackers for that user when we switch. + reloadTrackers(mCurrentUser); } else { reloadTrackers(); } } }, filter); - if (!android.app.Flags.modesHsum()) { + + if (android.app.Flags.modesHsum()) { + reloadTrackers(UserHandle.SYSTEM); + } else { reloadTrackers(); } } @@ -138,8 +138,10 @@ public class EventConditionProvider extends SystemConditionProviderService { @Override public void onUserSwitched(UserHandle user) { if (DEBUG) Slog.d(TAG, "onUserSwitched: " + user); - mCurrentUser = user; - reloadTrackers(user); + if (mCurrentUser.getIdentifier() != user.getIdentifier()) { + mCurrentUser = user; + reloadTrackers(user); + } } @Override @@ -274,12 +276,23 @@ public class EventConditionProvider extends SystemConditionProviderService { final int userId = EventInfo.resolveUserId(event.userId); final CalendarTracker tracker = mTrackers.get(userId); if (tracker == null) { - Slog.w(TAG, "No calendar tracker found for user " + userId); + Slog.w(TAG, + "No calendar tracker found for user " + userId + " and calendar = " + + event.calName); conditionsToNotify.add(createCondition(conditionId, Condition.STATE_FALSE)); continue; } result = tracker.checkEvent(event, now); } + + if (result == null) { + Slog.e(TAG, "No CheckEventResult for userId=" + event.userId + ", calId=" + + event.calendarId + ", calName=" + event.calName + + "; trackers count is " + mTrackers.size()); + conditionsToNotify.add(createCondition(conditionId, Condition.STATE_FALSE)); + continue; + } + if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) { reevaluateAt = result.recheckAt; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 48cc03221124..abf3da45d0cb 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -49,10 +49,6 @@ import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.Notification.FLAG_PROMOTED_ONGOING; import static android.app.Notification.FLAG_USER_INITIATED_JOB; import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT; -import static android.app.NotificationChannel.NEWS_ID; -import static android.app.NotificationChannel.PROMOTIONS_ID; -import static android.app.NotificationChannel.RECS_ID; -import static android.app.NotificationChannel.SOCIAL_MEDIA_ID; import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED; @@ -108,9 +104,7 @@ import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.Adjustment.KEY_TYPE; import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; -import static android.service.notification.Adjustment.TYPE_NEWS; import static android.service.notification.Adjustment.TYPE_PROMOTION; -import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; import static android.service.notification.Flags.callstyleCallbackApi; import static android.service.notification.Flags.notificationClassification; import static android.service.notification.Flags.notificationForceGrouping; @@ -6924,21 +6918,19 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") @Nullable + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) private NotificationChannel getClassificationChannelLocked(NotificationRecord r, Bundle adjustments) { int type = adjustments.getInt(KEY_TYPE); - if (TYPE_NEWS == type) { - return mPreferencesHelper.getNotificationChannel( - r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false); - } else if (TYPE_PROMOTION == type) { - return mPreferencesHelper.getNotificationChannel( - r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false); - } else if (TYPE_SOCIAL_MEDIA == type) { - return mPreferencesHelper.getNotificationChannel( - r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false); - } else if (TYPE_CONTENT_RECOMMENDATION == type) { - return mPreferencesHelper.getNotificationChannel( - r.getSbn().getPackageName(), r.getUid(), RECS_ID, false); + if (type >= TYPE_PROMOTION && type <= TYPE_CONTENT_RECOMMENDATION) { + NotificationChannel channel = mPreferencesHelper.getReservedChannel( + r.getSbn().getPackageName(), r.getUid(), type); + if (channel == null) { + channel = mPreferencesHelper.createReservedChannel( + r.getSbn().getPackageName(), r.getUid(), type); + handleSavePolicyFile(); + } + return channel; } return null; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index c9edc4106943..d26a5aa5491f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -17,6 +17,7 @@ package com.android.server.notification; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; +import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; import static android.app.NotificationChannel.NEWS_ID; import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationChannel.PROMOTIONS_ID; @@ -32,6 +33,10 @@ import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.os.UserHandle.USER_SYSTEM; +import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; +import static android.service.notification.Adjustment.TYPE_NEWS; +import static android.service.notification.Adjustment.TYPE_PROMOTION; +import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; import static android.service.notification.Flags.notificationClassification; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; @@ -66,6 +71,7 @@ import android.os.Process; import android.os.UserHandle; import android.permission.PermissionManager; import android.provider.Settings; +import android.service.notification.Adjustment; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; import android.service.notification.RankingHelperProto; @@ -440,6 +446,10 @@ public class PreferencesHelper implements RankingConfig { PackagePreferences r) { try { String id = parser.getAttributeValue(null, ATT_ID); + if (!notificationClassification() && SYSTEM_RESERVED_IDS.contains(id)) { + // delete bundle channels if flag is rolled back + return; + } String channelName = parser.getAttributeValue(null, ATT_NAME); int channelImportance = parser.getAttributeInt( null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); @@ -545,10 +555,6 @@ public class PreferencesHelper implements RankingConfig { Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e); } - if (notificationClassification()) { - addReservedChannelsLocked(r); - } - if (r.uid == UNKNOWN_UID) { if (Flags.persistIncompleteRestoreData()) { r.userId = userId; @@ -583,7 +589,7 @@ public class PreferencesHelper implements RankingConfig { private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws PackageManager.NameNotFoundException { - if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) { + if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) { // Not present return false; } @@ -594,7 +600,7 @@ public class PreferencesHelper implements RankingConfig { } // Remove Default Channel. - r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID); + r.channels.remove(DEFAULT_CHANNEL_ID); return true; } @@ -605,8 +611,8 @@ public class PreferencesHelper implements RankingConfig { return false; } - if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) { - r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString( + if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) { + r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString( com.android.internal.R.string.default_notification_channel_label)); return false; } @@ -619,7 +625,7 @@ public class PreferencesHelper implements RankingConfig { // Create Default Channel NotificationChannel channel; channel = new NotificationChannel( - NotificationChannel.DEFAULT_CHANNEL_ID, + DEFAULT_CHANNEL_ID, mContext.getString(R.string.default_notification_channel_label), r.importance); channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX); @@ -638,38 +644,25 @@ public class PreferencesHelper implements RankingConfig { return true; } - private void addReservedChannelsLocked(PackagePreferences p) { - if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) { - NotificationChannel channel = new NotificationChannel( - NotificationChannel.PROMOTIONS_ID, - mContext.getString(R.string.promotional_notification_channel_label), - IMPORTANCE_LOW); - p.channels.put(channel.getId(), channel); - } - - if (!p.channels.containsKey(NotificationChannel.RECS_ID)) { - NotificationChannel channel = new NotificationChannel( - NotificationChannel.RECS_ID, - mContext.getString(R.string.recs_notification_channel_label), - IMPORTANCE_LOW); - p.channels.put(channel.getId(), channel); - } - - if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) { - NotificationChannel channel = new NotificationChannel( - NotificationChannel.NEWS_ID, - mContext.getString(R.string.news_notification_channel_label), - IMPORTANCE_LOW); - p.channels.put(channel.getId(), channel); - } - - if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) { - NotificationChannel channel = new NotificationChannel( - NotificationChannel.SOCIAL_MEDIA_ID, - mContext.getString(R.string.social_notification_channel_label), - IMPORTANCE_LOW); - p.channels.put(channel.getId(), channel); + private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) { + String label = ""; + switch (channelId) { + case PROMOTIONS_ID: + label = mContext.getString(R.string.promotional_notification_channel_label); + break; + case RECS_ID: + label = mContext.getString(R.string.recs_notification_channel_label); + break; + case NEWS_ID: + label = mContext.getString(R.string.news_notification_channel_label); + break; + case SOCIAL_MEDIA_ID: + label = mContext.getString(R.string.social_notification_channel_label); + break; } + NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW); + p.channels.put(channelId, channel); + return channel; } public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { @@ -1074,7 +1067,7 @@ public class PreferencesHelper implements RankingConfig { if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) { throw new IllegalArgumentException("NotificationChannelGroup doesn't exist"); } - if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) { + if (DEFAULT_CHANNEL_ID.equals(channel.getId())) { throw new IllegalArgumentException("Reserved id"); } // Only the user can update bundle channel settings @@ -1407,6 +1400,54 @@ public class PreferencesHelper implements RankingConfig { } } + private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) { + switch (type) { + case TYPE_CONTENT_RECOMMENDATION: + return RECS_ID; + case TYPE_NEWS: + return NEWS_ID; + case TYPE_PROMOTION: + return PROMOTIONS_ID; + case TYPE_SOCIAL_MEDIA: + return SOCIAL_MEDIA_ID; + } + return null; + } + + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public NotificationChannel getReservedChannel(String pkg, int uid, + @Adjustment.Types int type) { + if (!notificationClassification()) { + return null; + } + Objects.requireNonNull(pkg); + String channelId = getChannelIdForBundleType(type); + if (channelId == null) { + return null; + } + NotificationChannel channel = + getConversationNotificationChannel(pkg, uid, channelId, null, true, false); + return channel; + } + + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public NotificationChannel createReservedChannel(String pkg, int uid, + @Adjustment.Types int type) { + if (!notificationClassification()) { + return null; + } + Objects.requireNonNull(pkg); + PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); + if (r == null) { + return null; + } + String channelId = getChannelIdForBundleType(type); + if (channelId == null) { + return null; + } + return addReservedChannelLocked(r, channelId); + } + @Override public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted) { @@ -1425,7 +1466,7 @@ public class PreferencesHelper implements RankingConfig { return null; } if (channelId == null) { - channelId = NotificationChannel.DEFAULT_CHANNEL_ID; + channelId = DEFAULT_CHANNEL_ID; } NotificationChannel channel = null; if (conversationId != null) { @@ -1536,7 +1577,7 @@ public class PreferencesHelper implements RankingConfig { int N = r.channels.size() - 1; for (int i = N; i >= 0; i--) { String key = r.channels.keyAt(i); - if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) { + if (!DEFAULT_CHANNEL_ID.equals(key)) { r.channels.remove(key); } } @@ -1654,10 +1695,7 @@ public class PreferencesHelper implements RankingConfig { && (activeChannelFilter == null || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE) || activeChannelFilter.contains(nc.getId())) - && !PROMOTIONS_ID.equals(nc.getId()) - && !NEWS_ID.equals(nc.getId()) - && !SOCIAL_MEDIA_ID.equals(nc.getId()) - && !RECS_ID.equals(nc.getId()); + && !SYSTEM_RESERVED_IDS.contains(nc.getId()); if (includeChannel) { if (nc.getGroup() != null) { if (r.groups.get(nc.getGroup()) != null) { @@ -1920,9 +1958,23 @@ public class PreferencesHelper implements RankingConfig { public boolean onlyHasDefaultChannel(String pkg, int uid) { synchronized (mLock) { PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); - if (r.channels.size() == (notificationClassification() ? 5 : 1) - && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) { - return true; + if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) { + if (r.channels.size() == 1) { + return true; + } + if (notificationClassification()) { + if (r.channels.size() <= 5) { + for (NotificationChannel c : r.channels.values()) { + if (!SYSTEM_RESERVED_IDS.contains(c.getId()) && + !DEFAULT_CHANNEL_ID.equals(c.getId())) { + return false; + } + return true; + } + } else { + return false; + } + } } return false; } @@ -2740,9 +2792,9 @@ public class PreferencesHelper implements RankingConfig { PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i); if (UserHandle.getUserId(PackagePreferences.uid) == userId) { if (PackagePreferences.channels.containsKey( - NotificationChannel.DEFAULT_CHANNEL_ID)) { + DEFAULT_CHANNEL_ID)) { PackagePreferences.channels.get( - NotificationChannel.DEFAULT_CHANNEL_ID).setName( + DEFAULT_CHANNEL_ID).setName( context.getResources().getString( R.string.default_notification_channel_label)); } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 24b090c6ffab..734c61b6b2a8 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -42,6 +42,7 @@ import com.android.server.notification.NotificationManagerService.DumpFilter; import com.android.server.pm.PackageManagerService; import java.io.PrintWriter; +import java.time.Clock; import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -62,6 +63,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { private static final String SCP_SETTING = "snoozed_schedule_condition_provider"; private final Context mContext = this; + private final Clock mClock; private final ArrayMap<Uri, ScheduleCalendar> mSubscriptions = new ArrayMap<>(); @GuardedBy("mSnoozedForAlarm") private final ArraySet<Uri> mSnoozedForAlarm = new ArraySet<>(); @@ -72,7 +74,13 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { private long mNextAlarmTime; public ScheduleConditionProvider() { + this(Clock.systemUTC()); + } + + @VisibleForTesting + ScheduleConditionProvider(Clock clock) { if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()"); + mClock = clock; } @Override @@ -86,7 +94,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { pw.print(" mConnected="); pw.println(mConnected); pw.print(" mRegistered="); pw.println(mRegistered); pw.println(" mSubscriptions="); - final long now = System.currentTimeMillis(); + final long now = mClock.millis(); synchronized (mSubscriptions) { for (Uri conditionId : mSubscriptions.keySet()) { pw.print(" "); @@ -117,7 +125,9 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { @Override public void onUserSwitched(UserHandle user) { - // Nothing to do because time-based schedules are not tied to any user data. + // Nothing to do here because evaluateSubscriptions() is called for the new configuration + // when users switch, and that will reevaluate the next alarm, which is the only piece that + // is user-dependent. } @Override @@ -151,12 +161,9 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } private void evaluateSubscriptions() { - if (mAlarmManager == null) { - mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - } - final long now = System.currentTimeMillis(); + final long now = mClock.millis(); mNextAlarmTime = 0; - long nextUserAlarmTime = getNextAlarm(); + long nextUserAlarmTime = getNextAlarmClockAlarm(); List<Condition> conditionsToNotify = new ArrayList<>(); synchronized (mSubscriptions) { setRegistered(!mSubscriptions.isEmpty()); @@ -232,7 +239,10 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } - public long getNextAlarm() { + private long getNextAlarmClockAlarm() { + if (mAlarmManager == null) { + mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + } final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock( ActivityManager.getCurrentUser()); return info != null ? info.getTriggerTime() : 0; @@ -252,8 +262,13 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(ACTION_EVALUATE); filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); - registerReceiver(mReceiver, filter, - Context.RECEIVER_EXPORTED_UNAUDITED); + if (android.app.Flags.modesHsum()) { + registerReceiverForAllUsers(mReceiver, filter, /* broadcastPermission= */ null, + /* scheduler= */ null); + } else { + registerReceiver(mReceiver, filter, + Context.RECEIVER_EXPORTED_UNAUDITED); + } } else { unregisterReceiver(mReceiver); } @@ -327,10 +342,18 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } } - private BroadcastReceiver mReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction()); + if (android.app.Flags.modesHsum()) { + if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction()) + && getSendingUserId() != ActivityManager.getCurrentUser()) { + // A different user changed their next alarm. + return; + } + } + if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { synchronized (mSubscriptions) { for (Uri conditionId : mSubscriptions.keySet()) { @@ -345,4 +368,8 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } }; + @VisibleForTesting // otherwise = NONE + public ArrayMap<Uri, ScheduleCalendar> getSubscriptions() { + return mSubscriptions; + } } diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java index 5ac883c58f03..9c24abcef123 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java @@ -316,6 +316,8 @@ public class DefaultCrossProfileIntentFiltersUtils { /* letsPersonalDataIntoProfile= */ true) .addAction(MediaStore.ACTION_IMAGE_CAPTURE) .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE) + .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE) + .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE) .addAction(MediaStore.ACTION_VIDEO_CAPTURE) .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION) .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) @@ -438,6 +440,8 @@ public class DefaultCrossProfileIntentFiltersUtils { /* letsPersonalDataIntoProfile= */ false) .addAction(MediaStore.ACTION_IMAGE_CAPTURE) .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE) + .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE) + .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE) .addAction(MediaStore.ACTION_VIDEO_CAPTURE) .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION) .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA) diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 8398ffc75f0d..f6e518a4fed7 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -397,7 +397,7 @@ final class DeletePackageHelper { try { executeDeletePackageLIF(action, packageName, deleteCodeAndResources, - allUserHandles, writeSettings); + allUserHandles, writeSettings, /* keepArtProfile= */ false); } catch (SystemDeleteException e) { if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: system deletion failure", e); return false; @@ -433,11 +433,11 @@ final class DeletePackageHelper { } public void executeDeletePackage(DeletePackageAction action, String packageName, - boolean deleteCodeAndResources, @NonNull int[] allUserHandles, boolean writeSettings) - throws SystemDeleteException { + boolean deleteCodeAndResources, @NonNull int[] allUserHandles, boolean writeSettings, + boolean keepArtProfile) throws SystemDeleteException { try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) { executeDeletePackageLIF(action, packageName, deleteCodeAndResources, allUserHandles, - writeSettings); + writeSettings, keepArtProfile); } } @@ -445,11 +445,14 @@ final class DeletePackageHelper { @GuardedBy("mPm.mInstallLock") private void executeDeletePackageLIF(DeletePackageAction action, String packageName, boolean deleteCodeAndResources, - @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException { + @NonNull int[] allUserHandles, boolean writeSettings, boolean keepArtProfile) + throws SystemDeleteException { final PackageSetting ps = action.mDeletingPs; final PackageRemovedInfo outInfo = action.mRemovedInfo; final UserHandle user = action.mUser; - final int flags = action.mFlags; + final int flags = + keepArtProfile ? action.mFlags | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES + : action.mFlags; final boolean systemApp = PackageManagerServiceUtils.isSystemApp(ps); // We need to get the permission state before package state is (potentially) destroyed. diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 83292b775ddc..355184e1c758 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1101,6 +1101,7 @@ final class InstallPackageHelper { request.setError(e.error, e.getMessage()); return; } + request.setKeepArtProfile(true); DexOptHelper.performDexoptIfNeeded(request, mDexManager, mContext, null); } } @@ -2406,7 +2407,7 @@ final class InstallPackageHelper { // Settings will be written during the call to updateSettingsLI(). mDeletePackageHelper.executeDeletePackage( reconciledPkg.mDeletePackageAction, packageName, - true, allUsers, false); + true, allUsers, false, installRequest.isKeepArtProfile()); } catch (SystemDeleteException e) { if (mPm.mIsEngBuild) { throw new RuntimeException("Unexpected failure", e); diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 089bbb7b0022..b0fe3a97af6e 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -169,6 +169,8 @@ final class InstallRequest { private final boolean mHasAppMetadataFileFromInstaller; + private boolean mKeepArtProfile = false; + // New install InstallRequest(InstallingSession params) { mUserId = params.getUser().getIdentifier(); @@ -1059,4 +1061,12 @@ final class InstallRequest { mPackageMetrics.onStepFinished(PackageMetrics.STEP_FREEZE_INSTALL); } } + + void setKeepArtProfile(boolean keepArtProfile) { + mKeepArtProfile = keepArtProfile; + } + + boolean isKeepArtProfile() { + return mKeepArtProfile; + } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f6a808b6c33e..a59f4bd8abe2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -39,6 +39,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; +import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -87,6 +88,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.ParcelableException; +import android.os.PermissionEnforcer; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; @@ -314,6 +316,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public PackageInstallerService(Context context, PackageManagerService pm, Supplier<PackageParser2> apexParserSupplier) { + super(PermissionEnforcer.fromContext(context)); + mContext = context; mPm = pm; @@ -1877,23 +1881,20 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } @Override + @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT) public @PackageInstaller.VerificationPolicy int getVerificationPolicy() { - if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the " - + "com.android.permission.VERIFICATION_AGENT permission " - + "to get the verification policy"); - } + getVerificationPolicy_enforcePermission(); return mVerificationPolicy.get(); } @Override + @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) { - if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the " - + "com.android.permission.VERIFICATION_AGENT permission " - + "to set the verification policy"); + setVerificationPolicy_enforcePermission(); + final int callingUid = getCallingUid(); + // Only the verifier currently bound by the system can change the policy, except for Shell + if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { + mVerifierController.assertCallerIsCurrentVerifier(callingUid); } if (!isValidVerificationPolicy(policy)) { return false; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 9a9e434c32d3..6ea5369040fe 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2884,14 +2884,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } // Send the request to the verifier and wait for its response before the rest of // the installation can proceed. + final VerifierCallback verifierCallback = new VerifierCallback(); if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId, sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo, declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null, - new VerifierCallback(), /* retry= */ false)) { - // A verifier is installed but cannot be connected. Installation disallowed. - onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, - "A verifier agent is available on device but cannot be connected.", - /* extras= */ null); + verifierCallback, /* retry= */ false)) { + // A verifier is installed but cannot be connected. + verifierCallback.onConnectionFailed(); } } else { // No need to check with verifier. Proceed with the rest of the verification. @@ -2995,7 +2994,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE, "A verifier agent is available on device but cannot be connected.", bundle); - }); } /** @@ -4447,9 +4445,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * @return the uid of the owner this session */ public int getInstallerUid() { - synchronized (mLock) { - return mInstallerUid; - } + return mInstallerUid; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4652c3af1185..f8e56e11735f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -399,6 +399,10 @@ class PackageManagerShellCommand extends ShellCommand { return runUnarchive(); case "get-domain-verification-agent": return runGetDomainVerificationAgent(); + case "get-verification-policy": + return runGetVerificationPolicy(); + case "set-verification-policy": + return runSetVerificationPolicy(); default: { if (ART_SERVICE_COMMANDS.contains(cmd)) { return runArtServiceCommand(); @@ -4645,6 +4649,86 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runGetVerificationPolicy() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + } else { + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runGetVerificationPolicy"); + try { + final IPackageInstaller installer = mInterface.getPackageInstaller(); + // TODO(b/360129657): global verification policy should be per user + final int policy = installer.getVerificationPolicy(); + pw.println(policy); + } catch (Exception e) { + pw.println("Failure [" + e.getMessage() + "]"); + return 1; + } + return 0; + } + + private int runSetVerificationPolicy() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) { + UserManagerInternal umi = + LocalServices.getService(UserManagerInternal.class); + UserInfo userInfo = umi.getUserInfo(userId); + if (userInfo == null) { + pw.println("Failure [user " + userId + " doesn't exist]"); + return 1; + } + } + } else { + pw.println("Error: Unknown option: " + opt); + return 1; + } + } + final String policyStr = getNextArg(); + if (policyStr == null) { + pw.println("Error: policy not specified"); + return 1; + } + final int translatedUserId = + translateUserId(userId, UserHandle.USER_SYSTEM, "runSetVerificationPolicy"); + try { + final IPackageInstaller installer = mInterface.getPackageInstaller(); + // TODO(b/360129657): global verification policy should be per user + final boolean success = installer.setVerificationPolicy(Integer.parseInt(policyStr)); + if (!success) { + pw.println("Failure setting verification policy."); + return 1; + } + } catch (Exception e) { + pw.println("Failure [" + e.getMessage() + "]"); + return 1; + } + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -5073,6 +5157,14 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" --user: return the agent of the given user (SYSTEM_USER if unspecified)"); pw.println(" get-package-storage-stats [--user <USER_ID>] <PACKAGE>"); pw.println(" Return the storage stats for the given app, if present"); + pw.println(" get-verification-policy [--user USER_ID]"); + pw.println(" Display current verification enforcement policy which will be applied to"); + pw.println(" all the future installation sessions"); + pw.println(" --user: show the policy of the given user (SYSTEM_USER if unspecified)"); + pw.println(" set-verification-policy POLICY [--user USER_ID]"); + pw.println(" Sets the verification policy of all the future installation sessions."); + pw.println(" --user: set the policy of the given user (SYSTEM_USER if unspecified)"); + pw.println(""); pw.println(""); printArtServiceHelp(); pw.println(""); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index f9a8968b8ad6..f01a74e8d60d 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -258,9 +258,10 @@ final class RemovePackageHelper { */ public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) { final String packageName = ps.getPackageName(); - // Step 1: always destroy app profiles. - mAppDataHelper.destroyAppProfilesLIF(packageName); - + // Step 1: always destroy app profiles except when explicitly preserved + if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) { + mAppDataHelper.destroyAppProfilesLIF(packageName); + } final AndroidPackage pkg; final SharedUserSetting sus; synchronized (mPm.mLock) { @@ -277,7 +278,8 @@ final class RemovePackageHelper { resolvedPkg = PackageImpl.buildFakeForDeletion(packageName, ps.getVolumeUuid()); } - int appDataDeletionFlags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL; + int appDataDeletionFlags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | (flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); // Personal data is preserved if the DELETE_KEEP_DATA flag is on if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) { if ((flags & PackageManager.DELETE_ARCHIVE) != 0) { diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java index a35618b309bb..78849d286ebe 100644 --- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java +++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java @@ -18,13 +18,12 @@ package com.android.server.pm.verify.pkg; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; @@ -114,10 +113,17 @@ public class VerifierController { private final Context mContext; private final Handler mHandler; + // Guards the remote service object, as well as the verifier name and UID, which should all be + // changed at the same time. + private final Object mLock = new Object(); @Nullable + @GuardedBy("mLock") private ServiceConnector<IVerifierService> mRemoteService; @Nullable + @GuardedBy("mLock") private ComponentName mRemoteServiceComponentName; + @GuardedBy("mLock") + private int mRemoteServiceUid = INVALID_UID; @NonNull private Injector mInjector; @@ -143,9 +149,11 @@ public class VerifierController { */ @Nullable public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) { - if (isVerifierConnected()) { - // Verifier is connected or is being connected, so it must be installed. - return mRemoteServiceComponentName.getPackageName(); + synchronized (mLock) { + if (isVerifierConnectedLocked()) { + // Verifier is connected or is being connected, so it must be installed. + return mRemoteServiceComponentName.getPackageName(); + } } // Verifier has been disconnected, or it hasn't been connected. Check if it's installed. return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId); @@ -178,16 +186,29 @@ public class VerifierController { } return true; } + Computer snapshot = snapshotSupplier.get(); Pair<ServiceConnector<IVerifierService>, ComponentName> result = - mInjector.getRemoteService(snapshotSupplier.get(), mContext, userId, mHandler); + mInjector.getRemoteService(snapshot, mContext, userId, mHandler); if (result == null || result.first == null) { if (DEBUG) { Slog.i(TAG, "Unable to find a qualified verifier."); } return false; } - mRemoteService = result.first; - mRemoteServiceComponentName = result.second; + final int verifierUid = snapshot.getPackageUidInternal( + result.second.getPackageName(), 0, userId, /* callingUid= */ SYSTEM_UID); + if (verifierUid == INVALID_UID) { + if (DEBUG) { + Slog.i(TAG, "Unable to find the UID of the qualified verifier."); + } + return false; + } + synchronized (mLock) { + mRemoteService = result.first; + mRemoteServiceComponentName = result.second; + mRemoteServiceUid = verifierUid; + } + if (DEBUG) { Slog.i(TAG, "Connecting to a qualified verifier: " + mRemoteServiceComponentName); } @@ -212,10 +233,13 @@ public class VerifierController { } private void destroy() { - if (isVerifierConnected()) { - mRemoteService.unbind(); - mRemoteService = null; - mRemoteServiceComponentName = null; + synchronized (mLock) { + if (isVerifierConnectedLocked()) { + mRemoteService.unbind(); + mRemoteService = null; + mRemoteServiceComponentName = null; + mRemoteServiceUid = INVALID_UID; + } } } }); @@ -223,7 +247,8 @@ public class VerifierController { return true; } - private boolean isVerifierConnected() { + @GuardedBy("mLock") + private boolean isVerifierConnectedLocked() { return mRemoteService != null && mRemoteServiceComponentName != null; } @@ -232,19 +257,21 @@ public class VerifierController { * requested for verification. */ public void notifyPackageNameAvailable(@NonNull String packageName) { - if (!isVerifierConnected()) { - if (DEBUG) { - Slog.i(TAG, "Verifier is not connected. Not notifying package name available"); + synchronized (mLock) { + if (!isVerifierConnectedLocked()) { + if (DEBUG) { + Slog.i(TAG, "Verifier is not connected. Not notifying package name available"); + } + return; } - return; + // Best effort. We don't check for the result. + mRemoteService.run(service -> { + if (DEBUG) { + Slog.i(TAG, "Notifying package name available for " + packageName); + } + service.onPackageNameAvailable(packageName); + }); } - // Best effort. We don't check for the result. - mRemoteService.run(service -> { - if (DEBUG) { - Slog.i(TAG, "Notifying package name available for " + packageName); - } - service.onPackageNameAvailable(packageName); - }); } /** @@ -253,27 +280,29 @@ public class VerifierController { * will no longer be requested for verification, possibly because the installation is canceled. */ public void notifyVerificationCancelled(@NonNull String packageName) { - if (!isVerifierConnected()) { - if (DEBUG) { - Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled"); + synchronized (mLock) { + if (!isVerifierConnectedLocked()) { + if (DEBUG) { + Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled"); + } + return; } - return; + // Best effort. We don't check for the result. + mRemoteService.run(service -> { + if (DEBUG) { + Slog.i(TAG, "Notifying verification cancelled for " + packageName); + } + service.onVerificationCancelled(packageName); + }); } - // Best effort. We don't check for the result. - mRemoteService.run(service -> { - if (DEBUG) { - Slog.i(TAG, "Notifying verification cancelled for " + packageName); - } - service.onVerificationCancelled(packageName); - }); } /** * Called to notify the bound verifier agent that a package that's pending installation needs * to be verified right now. * <p>The verification request must be sent to the verifier as soon as the verifier is - * connected. If the connection cannot be made within {@link #CONNECTION_TIMEOUT_SECONDS}</p> - * of when the request is sent out, we consider the verification to be failed and notify the + * connected. If the connection cannot be made within the specified time limit from + * when the request is sent out, we consider the verification to be failed and notify the * installation session.</p> * <p>If a response is not returned from the verifier agent within a timeout duration from the * time the request is sent to the verifier, the verification will be considered a failure.</p> @@ -291,43 +320,48 @@ public class VerifierController { if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) { return false; } - if (!isVerifierConnected()) { - if (DEBUG) { - Slog.i(TAG, "Verifier is not connected. Not notifying verification required"); - } - // Normally this should not happen because we just tried to bind. But if the verifier - // just crashed or just became unavailable, we should notify the installation session so - // it can finish with a verification failure. - return false; - } // For now, the verification id is the same as the installation session id. final int verificationId = installationSessionId; - final VerificationSession session = new VerificationSession( - /* id= */ verificationId, - /* installSessionId= */ installationSessionId, - packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams, - verificationPolicy, new VerificationSessionInterface(callback)); - AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> { - if (!retry) { - if (DEBUG) { - Slog.i(TAG, "Notifying verification required for session " + verificationId); - } - service.onVerificationRequired(session); - } else { + synchronized (mLock) { + if (!isVerifierConnectedLocked()) { if (DEBUG) { - Slog.i(TAG, "Notifying verification retry for session " + verificationId); + Slog.i(TAG, "Verifier is not connected. Not notifying verification required"); } - service.onVerificationRetry(session); - } - }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS) - .whenComplete((res, err) -> { - if (err != null) { - Slog.e(TAG, "Error notifying verification request for session " + verificationId, - err); - // Notify the installation session so it can finish with verification failure. - callback.onConnectionFailed(); + // Normally this should not happen because we just tried to bind. But if the + // verifier just crashed or just became unavailable, we should notify the + // installation session so it can finish with a verification failure. + return false; } - }); + final VerificationSession session = new VerificationSession( + /* id= */ verificationId, + /* installSessionId= */ installationSessionId, + packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams, + verificationPolicy, new VerificationSessionInterface(callback)); + AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> { + if (!retry) { + if (DEBUG) { + Slog.i(TAG, "Notifying verification required for session " + + verificationId); + } + service.onVerificationRequired(session); + } else { + if (DEBUG) { + Slog.i(TAG, "Notifying verification retry for session " + + verificationId); + } + service.onVerificationRetry(session); + } + }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS) + .whenComplete((res, err) -> { + if (err != null) { + Slog.e(TAG, "Error notifying verification request for session " + + verificationId, err); + // Notify the installation session so it can finish with verification + // failure. + callback.onConnectionFailed(); + } + }); + } // Keep track of the session status with the ID. Start counting down the session timeout. final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis(); final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis(); @@ -369,24 +403,27 @@ public class VerifierController { * Called to notify the bound verifier agent that a verification request has timed out. */ public void notifyVerificationTimeout(int verificationId) { - if (!isVerifierConnected()) { - if (DEBUG) { - Slog.i(TAG, - "Verifier is not connected. Not notifying timeout for " + verificationId); + synchronized (mLock) { + if (!isVerifierConnectedLocked()) { + if (DEBUG) { + Slog.i(TAG, + "Verifier is not connected. Not notifying timeout for " + + verificationId); + } + return; } - return; + AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> { + if (DEBUG) { + Slog.i(TAG, "Notifying timeout for " + verificationId); + } + service.onVerificationTimeout(verificationId); + }).whenComplete((res, err) -> { + if (err != null) { + Slog.e(TAG, "Error notifying VerificationTimeout for session " + + verificationId, err); + } + }); } - AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> { - if (DEBUG) { - Slog.i(TAG, "Notifying timeout for " + verificationId); - } - service.onVerificationTimeout(verificationId); - }).whenComplete((res, err) -> { - if (err != null) { - Slog.e(TAG, "Error notifying VerificationTimeout for session " - + verificationId, (Throwable) err); - } - }); } /** @@ -405,17 +442,19 @@ public class VerifierController { } } - @RequiresPermission(Manifest.permission.VERIFICATION_AGENT) - private void checkCallerPermission() { - // TODO: think of a better way to test it on non-eng builds - if (Build.IS_ENG) { - return; - } - if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the" - + " com.android.permission.VERIFICATION_AGENT permission" - + " to use VerificationSession APIs."); + /** + * Assert that the calling UID is the same as the UID of the currently connected verifier. + */ + public void assertCallerIsCurrentVerifier(int callingUid) { + synchronized (mLock) { + if (!isVerifierConnectedLocked()) { + throw new IllegalStateException( + "Unable to proceed because the verifier has been disconnected."); + } + if (callingUid != mRemoteServiceUid) { + throw new IllegalStateException( + "Calling uid " + callingUid + " is not the current verifier."); + } } } @@ -429,7 +468,7 @@ public class VerifierController { @Override public long getTimeoutTime(int verificationId) { - checkCallerPermission(); + assertCallerIsCurrentVerifier(getCallingUid()); synchronized (mVerificationStatus) { final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId); if (tracker == null) { @@ -442,7 +481,7 @@ public class VerifierController { @Override public long extendTimeRemaining(int verificationId, long additionalMs) { - checkCallerPermission(); + assertCallerIsCurrentVerifier(getCallingUid()); synchronized (mVerificationStatus) { final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId); if (tracker == null) { @@ -456,7 +495,7 @@ public class VerifierController { @Override public boolean setVerificationPolicy(int verificationId, @PackageInstaller.VerificationPolicy int policy) { - checkCallerPermission(); + assertCallerIsCurrentVerifier(getCallingUid()); synchronized (mVerificationStatus) { final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId); if (tracker == null) { @@ -469,7 +508,7 @@ public class VerifierController { @Override public void reportVerificationIncomplete(int id, int reason) { - checkCallerPermission(); + assertCallerIsCurrentVerifier(getCallingUid()); final VerificationStatusTracker tracker; synchronized (mVerificationStatus) { tracker = mVerificationStatus.get(id); @@ -484,15 +523,9 @@ public class VerifierController { } @Override - public void reportVerificationComplete(int id, VerificationStatus verificationStatus) { - reportVerificationCompleteWithExtensionResponse(id, verificationStatus, - /* extensionResponse= */ null); - } - - @Override - public void reportVerificationCompleteWithExtensionResponse(int id, - VerificationStatus verificationStatus, PersistableBundle extensionResponse) { - checkCallerPermission(); + public void reportVerificationComplete(int id, VerificationStatus verificationStatus, + @Nullable PersistableBundle extensionResponse) { + assertCallerIsCurrentVerifier(getCallingUid()); final VerificationStatusTracker tracker; synchronized (mVerificationStatus) { tracker = mVerificationStatus.get(id); diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java index 66ec53e6500e..a1236e533beb 100644 --- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java +++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java @@ -31,6 +31,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; import android.graphics.drawable.Icon; +import android.hardware.input.AppLaunchData; import android.hardware.input.KeyGestureEvent; import android.os.Handler; import android.os.RemoteException; @@ -48,6 +49,7 @@ import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IShortcutService; import com.android.internal.util.XmlUtils; @@ -63,6 +65,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -131,7 +134,10 @@ public class ModifierShortcutManager { private boolean mConsumeSearchKeyUp = true; private UserHandle mCurrentUser; private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>(); + @GuardedBy("mAppIntentCache") + private final Map<AppLaunchData, Intent> mAppIntentCache = new HashMap<>(); + @SuppressLint("MissingPermission") ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) { mContext = context; mHandler = handler; @@ -146,6 +152,17 @@ public class ModifierShortcutManager { } else { mRoleIntents.remove(roleName); } + synchronized (mAppIntentCache) { + mAppIntentCache.entrySet().removeIf( + entry -> { + if (entry.getKey() instanceof AppLaunchData.RoleData) { + return Objects.equals( + ((AppLaunchData.RoleData) entry.getKey()).getRole(), + roleName); + } + return false; + }); + } }, UserHandle.ALL); mCurrentUser = currentUser; mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); @@ -159,6 +176,10 @@ public class ModifierShortcutManager { // so clear the cache. clearRoleIntents(); clearComponentIntents(); + + synchronized (mAppIntentCache) { + mAppIntentCache.clear(); + } } void clearRoleIntents() { @@ -748,6 +769,46 @@ public class ModifierShortcutManager { shortcuts); } + private Intent getIntentFromAppLaunchData(@NonNull AppLaunchData data) { + Context context = mContext.createContextAsUser(mCurrentUser, 0); + synchronized (mAppIntentCache) { + Intent intent = mAppIntentCache.get(data); + if (intent != null) { + return intent; + } + if (data instanceof AppLaunchData.CategoryData) { + intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, + ((AppLaunchData.CategoryData) data).getCategory()); + } else if (data instanceof AppLaunchData.RoleData) { + intent = getRoleLaunchIntent(context, ((AppLaunchData.RoleData) data).getRole()); + } else if (data instanceof AppLaunchData.ComponentData) { + AppLaunchData.ComponentData componentData = (AppLaunchData.ComponentData) data; + intent = resolveComponentNameIntent(context, componentData.getPackageName(), + componentData.getClassName()); + } + if (intent != null) { + mAppIntentCache.put(data, intent); + } + return intent; + } + } + + boolean launchApplication(@NonNull AppLaunchData data) { + Intent intent = getIntentFromAppLaunchData(data); + if (intent == null) { + return false; + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivityAsUser(intent, mCurrentUser); + return true; + } catch (ActivityNotFoundException ex) { + Slog.w(TAG, "Not launching app because " + + "the activity to which it refers to was not found: " + data); + } + return false; + } + /** * Given an intent to launch an application and the character and shift state that should * trigger it, return a suitable {@link KeyboardShortcutInfo} that contains the label and @@ -869,7 +930,7 @@ public class ModifierShortcutManager { // TODO(b/280423320): Add new field package name associated in the // KeyboardShortcutEvent atom and log it accordingly. - return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME; + return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION; } @KeyGestureEvent.KeyGestureType diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index ad5c84026aa6..3ab1009b22e1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -84,6 +84,7 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable; +import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.modifierShortcutDump; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; @@ -148,6 +149,7 @@ import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; +import android.hardware.input.AppLaunchData; import android.hardware.input.InputManager; import android.hardware.input.KeyGestureEvent; import android.media.AudioManager; @@ -1615,7 +1617,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TRIPLE_PRESS_PRIMARY_NOTHING: break; case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: - mTalkbackShortcutController.toggleTalkback(mCurrentUserId); + mTalkbackShortcutController.toggleTalkback(mCurrentUserId, + TalkbackShortcutController.ShortcutSource.GESTURE); if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { performHapticFeedback(HapticFeedbackConstants.CONFIRM, "Stem primary - Triple Press - Toggle Accessibility"); @@ -3603,12 +3606,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } break; + case KeyEvent.KEYCODE_T: + if (keyboardA11yShortcutControl()) { + if (firstDown && event.isMetaPressed() && event.isAltPressed()) { + mTalkbackShortcutController.toggleTalkback(mCurrentUserId, + TalkbackShortcutController.ShortcutSource.KEYBOARD); + notifyKeyGestureCompleted(event, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); + return true; + } + } + break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (mEnableBugReportKeyboardShortcut && firstDown && event.isMetaPressed() && event.isCtrlPressed()) { try { - mActivityManagerService.requestInteractiveBugReport(); + if (!mActivityManagerService.launchBugReportHandlerApp()) { + mActivityManagerService.requestInteractiveBugReport(); + } } catch (RemoteException e) { Slog.d(TAG, "Error taking bugreport", e); } @@ -4022,6 +4038,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH: case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT: case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: + case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION: return true; case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD: case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD: @@ -4034,6 +4051,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD: return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController .isAccessibilityShortcutAvailable(false); + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: + return keyboardA11yShortcutControl(); default: return false; } @@ -4102,7 +4121,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT: if (complete && mEnableBugReportKeyboardShortcut) { try { - mActivityManagerService.requestInteractiveBugReport(); + if (!mActivityManagerService.launchBugReportHandlerApp()) { + mActivityManagerService.requestInteractiveBugReport(); + } } catch (RemoteException e) { Slog.d(TAG, "Error taking bugreport", e); } @@ -4251,6 +4272,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.closeSystemDialogs(); } return true; + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: + if (keyboardA11yShortcutControl()) { + if (complete) { + mTalkbackShortcutController.toggleTalkback(mCurrentUserId, + TalkbackShortcutController.ShortcutSource.KEYBOARD); + } + return true; + } + break; + case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION: + AppLaunchData data = event.getAppLaunchData(); + if (complete && isUserSetupComplete() && !keyguardOn + && data != null && mModifierShortcutManager.launchApplication(data)) { + dismissKeyboardShortcutsMenu(); + return true; + } + break; } return false; } @@ -6920,6 +6958,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (modifierShortcutManagerMultiuser()) { mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId)); } + mInputManagerInternal.setCurrentUser(newUserId); } @Override diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java index e544ae64521c..9e16a7d5e83a 100644 --- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java +++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java @@ -44,6 +44,11 @@ class TalkbackShortcutController { private final Context mContext; private final PackageManager mPackageManager; + public enum ShortcutSource { + GESTURE, + KEYBOARD, + } + TalkbackShortcutController(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); @@ -55,7 +60,7 @@ class TalkbackShortcutController { * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if * talkback is disabled */ - boolean toggleTalkback(int userId) { + boolean toggleTalkback(int userId, ShortcutSource source) { final Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); ComponentName componentName = getTalkbackComponent(); @@ -65,13 +70,13 @@ class TalkbackShortcutController { boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); - if (isTalkBackShortcutGestureEnabled()) { + if (source == ShortcutSource.KEYBOARD || isTalkBackShortcutGestureEnabled()) { isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, - isTalkbackAlreadyEnabled); + isTalkbackAlreadyEnabled, userId); // log stem triple press telemetry if it's a talkback enabled event. - if (isTalkbackAlreadyEnabled) { + if (source == ShortcutSource.GESTURE && isTalkbackAlreadyEnabled) { logStemTriplePressAccessibilityTelemetry(componentName); } } diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index dc6b1644db4d..78bc06c27130 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -31,6 +31,7 @@ import android.content.Context; import android.hardware.thermal.IThermal; import android.hardware.thermal.IThermalChangedCallback; import android.hardware.thermal.TemperatureThreshold; +import android.hardware.thermal.TemperatureType; import android.hardware.thermal.ThrottlingSeverity; import android.hardware.thermal.V1_0.ThermalStatus; import android.hardware.thermal.V1_0.ThermalStatusCode; @@ -134,6 +135,31 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher(); + private final ThermalHalWrapper.WrapperThermalChangedCallback mWrapperCallback = + new ThermalHalWrapper.WrapperThermalChangedCallback() { + @Override + public void onTemperatureChanged(Temperature temperature) { + final long token = Binder.clearCallingIdentity(); + try { + ThermalManagerService.this.onTemperatureChanged(temperature, true); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void onThresholdChanged(TemperatureThreshold threshold) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mTemperatureWatcher.mSamples) { + mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + }; + private final Context mContext; public ThermalManagerService(Context context) { @@ -146,7 +172,7 @@ public class ThermalManagerService extends SystemService { mContext = context; mHalWrapper = halWrapper; if (halWrapper != null) { - halWrapper.setCallback(this::onTemperatureChangedCallback); + halWrapper.setCallback(mWrapperCallback); } mStatus = Temperature.THROTTLING_NONE; } @@ -171,19 +197,19 @@ public class ThermalManagerService extends SystemService { // Connect to HAL and post to listeners. boolean halConnected = (mHalWrapper != null); if (!halConnected) { - mHalWrapper = new ThermalHalAidlWrapper(this::onTemperatureChangedCallback); + mHalWrapper = new ThermalHalAidlWrapper(mWrapperCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { - mHalWrapper = new ThermalHal20Wrapper(this::onTemperatureChangedCallback); + mHalWrapper = new ThermalHal20Wrapper(mWrapperCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { - mHalWrapper = new ThermalHal11Wrapper(this::onTemperatureChangedCallback); + mHalWrapper = new ThermalHal11Wrapper(mWrapperCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { - mHalWrapper = new ThermalHal10Wrapper(this::onTemperatureChangedCallback); + mHalWrapper = new ThermalHal10Wrapper(mWrapperCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { @@ -200,7 +226,7 @@ public class ThermalManagerService extends SystemService { onTemperatureChanged(temperatures.get(i), false); } onTemperatureMapChangedLocked(); - mTemperatureWatcher.updateThresholds(); + mTemperatureWatcher.getAndUpdateThresholds(); mHalReady.set(true); } } @@ -335,16 +361,6 @@ public class ThermalManagerService extends SystemService { } } - /* HwBinder callback **/ - private void onTemperatureChangedCallback(Temperature temperature) { - final long token = Binder.clearCallingIdentity(); - try { - onTemperatureChanged(temperature, true); - } finally { - Binder.restoreCallingIdentity(token); - } - } - private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); if (statsManager != null) { @@ -924,19 +940,19 @@ public class ThermalManagerService extends SystemService { /** Lock to protect HAL handle. */ protected final Object mHalLock = new Object(); - @FunctionalInterface - interface TemperatureChangedCallback { - void onValues(Temperature temperature); + interface WrapperThermalChangedCallback { + void onTemperatureChanged(Temperature temperature); + void onThresholdChanged(TemperatureThreshold threshold); } /** Temperature callback. */ - protected TemperatureChangedCallback mCallback; + protected WrapperThermalChangedCallback mCallback; /** Cookie for matching the right end point. */ protected static final int THERMAL_HAL_DEATH_COOKIE = 5612; @VisibleForTesting - protected void setCallback(TemperatureChangedCallback cb) { + protected void setCallback(WrapperThermalChangedCallback cb) { mCallback = cb; } @@ -959,7 +975,7 @@ public class ThermalManagerService extends SystemService { List<Temperature> temperatures = getCurrentTemperatures(false, 0); final int count = temperatures.size(); for (int i = 0; i < count; i++) { - mCallback.onValues(temperatures.get(i)); + mCallback.onTemperatureChanged(temperatures.get(i)); } } } @@ -985,31 +1001,42 @@ public class ThermalManagerService extends SystemService { private IThermal mInstance = null; /** Callback for Thermal HAL AIDL. */ - private final IThermalChangedCallback mThermalChangedCallback = + private final IThermalChangedCallback mThermalCallbackAidl = new IThermalChangedCallback.Stub() { - @Override public void notifyThrottling( - android.hardware.thermal.Temperature temperature) - throws RemoteException { + @Override + public void notifyThrottling( + android.hardware.thermal.Temperature temperature) { Temperature svcTemperature = new Temperature(temperature.value, temperature.type, temperature.name, temperature.throttlingStatus); final long token = Binder.clearCallingIdentity(); try { - mCallback.onValues(svcTemperature); + mCallback.onTemperatureChanged(svcTemperature); } finally { Binder.restoreCallingIdentity(token); } } - @Override public int getInterfaceVersion() throws RemoteException { - return this.VERSION; - } + @Override + public void notifyThresholdChanged(TemperatureThreshold threshold) { + if (Flags.allowThermalThresholdsCallback()) { + if (threshold.type == TemperatureType.SKIN) { + mCallback.onThresholdChanged(threshold); + } + } + } - @Override public String getInterfaceHash() throws RemoteException { - return this.HASH; - } - }; + @Override + public int getInterfaceVersion() throws RemoteException { + return this.VERSION; + } - ThermalHalAidlWrapper(TemperatureChangedCallback callback) { + @Override + public String getInterfaceHash() throws RemoteException { + return this.HASH; + } + }; + + ThermalHalAidlWrapper(WrapperThermalChangedCallback callback) { mCallback = callback; } @@ -1153,7 +1180,7 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting void registerThermalChangedCallback() { try { - mInstance.registerThermalChangedCallback(mThermalChangedCallback); + mInstance.registerThermalChangedCallback(mThermalCallbackAidl); } catch (IllegalArgumentException | IllegalStateException e) { Slog.e(TAG, "Couldn't registerThermalChangedCallback due to invalid status", e); @@ -1185,7 +1212,7 @@ public class ThermalManagerService extends SystemService { @GuardedBy("mHalLock") private android.hardware.thermal.V1_0.IThermal mThermalHal10 = null; - ThermalHal10Wrapper(TemperatureChangedCallback callback) { + ThermalHal10Wrapper(WrapperThermalChangedCallback callback) { mCallback = callback; } @@ -1317,14 +1344,14 @@ public class ThermalManagerService extends SystemService { : Temperature.THROTTLING_NONE); final long token = Binder.clearCallingIdentity(); try { - mCallback.onValues(thermalSvcTemp); + mCallback.onTemperatureChanged(thermalSvcTemp); } finally { Binder.restoreCallingIdentity(token); } } }; - ThermalHal11Wrapper(TemperatureChangedCallback callback) { + ThermalHal11Wrapper(WrapperThermalChangedCallback callback) { mCallback = callback; } @@ -1455,14 +1482,14 @@ public class ThermalManagerService extends SystemService { temperature.throttlingStatus); final long token = Binder.clearCallingIdentity(); try { - mCallback.onValues(thermalSvcTemp); + mCallback.onTemperatureChanged(thermalSvcTemp); } finally { Binder.restoreCallingIdentity(token); } } }; - ThermalHal20Wrapper(TemperatureChangedCallback callback) { + ThermalHal20Wrapper(WrapperThermalChangedCallback callback) { mCallback = callback; } @@ -1627,52 +1654,57 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS; - void updateThresholds() { + void getAndUpdateThresholds() { List<TemperatureThreshold> thresholds = mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN); synchronized (mSamples) { if (Flags.allowThermalHeadroomThresholds()) { Arrays.fill(mHeadroomThresholds, Float.NaN); } - for (int t = 0; t < thresholds.size(); ++t) { - TemperatureThreshold threshold = thresholds.get(t); - if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) { - continue; - } - float severeThreshold = - threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]; - if (!Float.isNaN(severeThreshold)) { - mSevereThresholds.put(threshold.name, severeThreshold); - if (Flags.allowThermalHeadroomThresholds()) { - for (int severity = ThrottlingSeverity.LIGHT; - severity <= ThrottlingSeverity.SHUTDOWN; severity++) { - if (threshold.hotThrottlingThresholds.length > severity) { - updateHeadroomThreshold(severity, - threshold.hotThrottlingThresholds[severity], - severeThreshold); - } - } - } - } + for (final TemperatureThreshold threshold : thresholds) { + updateTemperatureThresholdLocked(threshold, false); } } } // For an older device with multiple SKIN sensors, we will set a severity's headroom - // threshold based on the minimum value of all as a workaround. - void updateHeadroomThreshold(int severity, float threshold, float severeThreshold) { - if (!Float.isNaN(threshold)) { - synchronized (mSamples) { - if (severity == ThrottlingSeverity.SEVERE) { - mHeadroomThresholds[severity] = 1.0f; - return; + // threshold based on the minimum value of all as a workaround, unless override. + @GuardedBy("mSamples") + void updateTemperatureThresholdLocked(TemperatureThreshold threshold, boolean override) { + if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) { + return; + } + float severeThreshold = + threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]; + if (Float.isNaN(severeThreshold)) { + return; + } + mSevereThresholds.put(threshold.name, severeThreshold); + if (!Flags.allowThermalHeadroomThresholds()) { + return; + } + if (override) { + Arrays.fill(mHeadroomThresholds, Float.NaN); + } + for (int severity = ThrottlingSeverity.LIGHT; + severity <= ThrottlingSeverity.SHUTDOWN; severity++) { + if (threshold.hotThrottlingThresholds.length > severity) { + float t = threshold.hotThrottlingThresholds[severity]; + if (Float.isNaN(t)) { + continue; } - float headroom = normalizeTemperature(threshold, severeThreshold); - if (Float.isNaN(mHeadroomThresholds[severity])) { - mHeadroomThresholds[severity] = headroom; - } else { - float lastHeadroom = mHeadroomThresholds[severity]; - mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom); + synchronized (mSamples) { + if (severity == ThrottlingSeverity.SEVERE) { + mHeadroomThresholds[severity] = 1.0f; + continue; + } + float headroom = normalizeTemperature(t, severeThreshold); + if (Float.isNaN(mHeadroomThresholds[severity])) { + mHeadroomThresholds[severity] = headroom; + } else { + float lastHeadroom = mHeadroomThresholds[severity]; + mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom); + } } } } diff --git a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java index c6b260288a88..64f0693f14c4 100644 --- a/services/core/java/com/android/server/power/WakefulnessSessionObserver.java +++ b/services/core/java/com/android/server/power/WakefulnessSessionObserver.java @@ -49,6 +49,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.IndentingPrintWriter; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayAddress; @@ -70,12 +71,11 @@ import java.lang.annotation.RetentionPolicy; public class WakefulnessSessionObserver { private static final String TAG = "WakefulnessSessionObserver"; - private static final int OFF_REASON_UNKNOWN = FrameworkStatsLog + static final int OFF_REASON_UNKNOWN = FrameworkStatsLog .SCREEN_INTERACTIVE_SESSION_REPORTED__INTERACTIVE_STATE_OFF_REASON__UNKNOWN; - private static final int OFF_REASON_TIMEOUT = FrameworkStatsLog + static final int OFF_REASON_TIMEOUT = FrameworkStatsLog .SCREEN_INTERACTIVE_SESSION_REPORTED__INTERACTIVE_STATE_OFF_REASON__TIMEOUT; - @VisibleForTesting - protected static final int OFF_REASON_POWER_BUTTON = FrameworkStatsLog + static final int OFF_REASON_POWER_BUTTON = FrameworkStatsLog .SCREEN_INTERACTIVE_SESSION_REPORTED__INTERACTIVE_STATE_OFF_REASON__POWER_BUTTON; /** @@ -90,25 +90,21 @@ public class WakefulnessSessionObserver { @Retention(RetentionPolicy.SOURCE) private @interface OffReason {} - private static final int OVERRIDE_OUTCOME_UNKNOWN = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_UNKNOWN = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__UNKNOWN; - @VisibleForTesting - protected static final int OVERRIDE_OUTCOME_TIMEOUT_SUCCESS = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_TIMEOUT_SUCCESS = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__TIMEOUT_SUCCESS; - @VisibleForTesting - protected static final int OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__TIMEOUT_USER_INITIATED_REVERT; - private static final int OVERRIDE_OUTCOME_CANCEL_CLIENT_API_CALL = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_CANCEL_CLIENT_API_CALL = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__CANCEL_CLIENT_API_CALL; - @VisibleForTesting - protected static final int OVERRIDE_OUTCOME_CANCEL_USER_INTERACTION = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_CANCEL_USER_INTERACTION = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__CANCEL_USER_INTERACTION; - @VisibleForTesting - protected static final int OVERRIDE_OUTCOME_CANCEL_POWER_BUTTON = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_CANCEL_POWER_BUTTON = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__CANCEL_POWER_BUTTON; - private static final int OVERRIDE_OUTCOME_CANCEL_CLIENT_DISCONNECT = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_CANCEL_CLIENT_DISCONNECT = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__CANCEL_CLIENT_DISCONNECTED; - private static final int OVERRIDE_OUTCOME_CANCEL_OTHER = FrameworkStatsLog + static final int OVERRIDE_OUTCOME_CANCEL_OTHER = FrameworkStatsLog .SCREEN_TIMEOUT_OVERRIDE_REPORTED__OVERRIDE_OUTCOME__CANCEL_OTHER; /** @@ -128,19 +124,15 @@ public class WakefulnessSessionObserver { @Retention(RetentionPolicy.SOURCE) private @interface OverrideOutcome {} - private static final int POLICY_REASON_UNKNOWN = FrameworkStatsLog + static final int POLICY_REASON_UNKNOWN = FrameworkStatsLog .SCREEN_DIM_REPORTED__POLICY_REASON__UNKNOWN; - @VisibleForTesting - protected static final int POLICY_REASON_OFF_TIMEOUT = FrameworkStatsLog + static final int POLICY_REASON_OFF_TIMEOUT = FrameworkStatsLog .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_TIMEOUT; - @VisibleForTesting - protected static final int POLICY_REASON_OFF_POWER_BUTTON = FrameworkStatsLog + static final int POLICY_REASON_OFF_POWER_BUTTON = FrameworkStatsLog .SCREEN_DIM_REPORTED__POLICY_REASON__OFF_POWER_BUTTON; - @VisibleForTesting - protected static final int POLICY_REASON_BRIGHT_UNDIM = FrameworkStatsLog + static final int POLICY_REASON_BRIGHT_UNDIM = FrameworkStatsLog .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_UNDIM; - @VisibleForTesting - protected static final int POLICY_REASON_BRIGHT_INITIATED_REVERT = FrameworkStatsLog + static final int POLICY_REASON_BRIGHT_INITIATED_REVERT = FrameworkStatsLog .SCREEN_DIM_REPORTED__POLICY_REASON__BRIGHT_INITIATED_REVERT; /** @@ -157,21 +149,18 @@ public class WakefulnessSessionObserver { @Retention(RetentionPolicy.SOURCE) private @interface PolicyReason {} - @VisibleForTesting protected static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER; - private static final long USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L; - private static final long SEND_OVERRIDE_TIMEOUT_LOG_THRESHOLD_MILLIS = 1000L; - @VisibleForTesting - protected static final long SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS = 500L; + static final int DEFAULT_USER_ACTIVITY = USER_ACTIVITY_EVENT_OTHER; + static final long USER_INITIATED_REVERT_THRESHOLD_MILLIS = 5000L; + static final long SEND_OVERRIDE_TIMEOUT_LOG_THRESHOLD_MILLIS = 1000L; + static final long SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS = 500L; - @VisibleForTesting protected static final Object HANDLER_TOKEN = new Object(); + static final Object HANDLER_TOKEN = new Object(); private Context mContext; private int mScreenOffTimeoutMs; private int mOverrideTimeoutMs = 0; - @VisibleForTesting - protected final SparseArray<WakefulnessSessionPowerGroup> mPowerGroups = new SparseArray<>(); - @VisibleForTesting - protected WakefulnessSessionFrameworkStatsLogger mWakefulnessSessionFrameworkStatsLogger; + final SparseArray<WakefulnessSessionPowerGroup> mPowerGroups = new SparseArray<>(); + WakefulnessSessionFrameworkStatsLogger mWakefulnessSessionFrameworkStatsLogger; private final Clock mClock; private final Object mLock = new Object(); private final Handler mHandler; @@ -347,7 +336,8 @@ public class WakefulnessSessionObserver { writer.println(); } - private void updateSettingScreenOffTimeout(Context context) { + @VisibleForTesting + void updateSettingScreenOffTimeout(Context context) { synchronized (mLock) { mScreenOffTimeoutMs = Settings.System.getIntForUser( context.getContentResolver(), @@ -453,6 +443,7 @@ public class WakefulnessSessionObserver { return; } + final int screenOffTimeoutMs = getScreenOffTimeout(); mIsInteractive = isInteractive(wakefulness); if (mIsInteractive) { mInteractiveStateOnStartTimestamp = eventTime; @@ -466,7 +457,7 @@ public class WakefulnessSessionObserver { mPowerGroupId, OVERRIDE_OUTCOME_TIMEOUT_USER_INITIATED_REVERT, mOverrideTimeoutMs, - getScreenOffTimeout()); + screenOffTimeoutMs); mSendOverrideTimeoutLogTimestamp = eventTime; } mTimeoutOffTimestamp = TIMEOUT_OFF_RESET_TIMESTAMP; @@ -496,7 +487,7 @@ public class WakefulnessSessionObserver { mPowerGroupId, OVERRIDE_OUTCOME_CANCEL_POWER_BUTTON, mOverrideTimeoutMs, - getScreenOffTimeout()); + screenOffTimeoutMs); mSendOverrideTimeoutLogTimestamp = eventTime; mTimeoutOverrideReleaseReason = RELEASE_REASON_UNKNOWN; // reset the reason } @@ -514,13 +505,12 @@ public class WakefulnessSessionObserver { // timeout has been done successfully. if (isInOverrideTimeout()) { reducedInteractiveStateOnDurationMs = - getScreenOffTimeout() - mOverrideTimeoutMs; - + screenOffTimeoutMs - mOverrideTimeoutMs; mWakefulnessSessionFrameworkStatsLogger.logTimeoutOverrideEvent( mPowerGroupId, OVERRIDE_OUTCOME_TIMEOUT_SUCCESS, mOverrideTimeoutMs, - getScreenOffTimeout()); + screenOffTimeoutMs); mSendOverrideTimeoutLogTimestamp = eventTime; // Record a timestamp to track if the user initiates to revert from off @@ -533,13 +523,21 @@ public class WakefulnessSessionObserver { long interactiveStateOnDurationMs = eventTime - mInteractiveStateOnStartTimestamp; - mWakefulnessSessionFrameworkStatsLogger.logSessionEvent( - mPowerGroupId, - interactiveStateOffReason, - interactiveStateOnDurationMs, - lastUserActivity, - lastUserActivityDurationMs, - reducedInteractiveStateOnDurationMs); + + if (reducedInteractiveStateOnDurationMs < screenOffTimeoutMs + && reducedInteractiveStateOnDurationMs >= 0) { + mWakefulnessSessionFrameworkStatsLogger.logSessionEvent( + mPowerGroupId, + interactiveStateOffReason, + interactiveStateOnDurationMs, + lastUserActivity, + lastUserActivityDurationMs, + reducedInteractiveStateOnDurationMs); + } else { + Slog.w(TAG, "invalid reducedInteractiveStateOnDurationMs: " + + reducedInteractiveStateOnDurationMs); + } + } } @@ -608,6 +606,7 @@ public class WakefulnessSessionObserver { return; } + final int screenOffTimeoutMs = getScreenOffTimeout(); int dimDurationMs = 0; int lastUserActivity = mCurrentUserActivityEvent; int lastUserActivityDurationMs = (int) (eventTime - mCurrentUserActivityTimestamp); @@ -625,7 +624,7 @@ public class WakefulnessSessionObserver { lastUserActivity, lastUserActivityDurationMs, dimDurationMs, - mScreenOffTimeoutMs); + screenOffTimeoutMs); mPastDimDurationMs = dimDurationMs; return; } @@ -645,7 +644,7 @@ public class WakefulnessSessionObserver { lastUserActivity, lastUserActivityDurationMs, dimDurationMs, - mScreenOffTimeoutMs); + screenOffTimeoutMs); mHandler.removeCallbacksAndMessages(HANDLER_TOKEN); } @@ -674,7 +673,7 @@ public class WakefulnessSessionObserver { savedLastUserActivity, savedLastUserActivityDurationMs, savedDimDurationMs, - mScreenOffTimeoutMs); + screenOffTimeoutMs); mPastDimDurationMs = savedDimDurationMs; }, HANDLER_TOKEN, SCREEN_POLICY_DIM_POWER_OFF_BRIGHT_THRESHOLD_MILLIS); } @@ -692,7 +691,7 @@ public class WakefulnessSessionObserver { lastUserActivity, lastUserActivityDurationMs, mPastDimDurationMs, - mScreenOffTimeoutMs); + screenOffTimeoutMs); } return; } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index 68760aae8d9d..4d63fdf45704 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -959,9 +959,10 @@ public class BatterySaverStateMachine { .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra(EXTRA_SHOW_FRAGMENT_TITLE, highlightBundle); - PendingIntent batterySaverIntent = PendingIntent.getActivity( + PendingIntent batterySaverIntent = PendingIntent.getActivityAsUser( mContext, 0 /* requestCode */, intent, - PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, + null /* options */, UserHandle.CURRENT); final String title = res.getString(titleId); final String summary = res.getString(summaryId); diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index b996c43d3dd5..600fe59215b6 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -26,6 +26,7 @@ import android.os.BatteryUsageStatsQuery; import android.os.Handler; import android.os.Process; import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.os.Clock; @@ -350,13 +351,14 @@ public class BatteryUsageStatsProvider { synchronized (stats) { final List<PowerCalculator> powerCalculators = getPowerCalculators(); - if (!powerCalculators.isEmpty()) { - if (monotonicStartTime != MonotonicClock.UNDEFINED - || monotonicEndTime != MonotonicClock.UNDEFINED) { - throw new IllegalStateException("BatteryUsageStatsQuery specifies a time " - + "range that is incompatible with PowerCalculators: " - + powerCalculators); - } + boolean usePowerCalculators = !powerCalculators.isEmpty(); + if (usePowerCalculators + && (monotonicStartTime != MonotonicClock.UNDEFINED + || monotonicEndTime != MonotonicClock.UNDEFINED)) { + Slog.wtfStack(TAG, "BatteryUsageStatsQuery specifies a time " + + "range that is incompatible with PowerCalculators: " + + powerCalculators); + usePowerCalculators = false; } if (monotonicStartTime == MonotonicClock.UNDEFINED) { @@ -371,7 +373,7 @@ public class BatteryUsageStatsProvider { batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs); } - if (!powerCalculators.isEmpty()) { + if (usePowerCalculators) { final long realtimeUs = mClock.elapsedRealtime() * 1000; final long uptimeUs = mClock.uptimeMillis() * 1000; final int[] powerComponents = query.getPowerComponents(); diff --git a/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java index ad1b4a7cc487..f5d10fdd2dd2 100644 --- a/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java @@ -20,12 +20,10 @@ import android.os.BatteryConsumer; import android.os.BatteryStats; import com.android.internal.os.PowerProfile; -import com.android.server.power.stats.PowerStatsUidResolver; class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor { - AudioPowerStatsProcessor(PowerProfile powerProfile, - PowerStatsUidResolver uidResolver) { - super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver, + AudioPowerStatsProcessor(PowerProfile powerProfile) { + super(BatteryConsumer.POWER_COMPONENT_AUDIO, powerProfile.getAveragePower(PowerProfile.POWER_AUDIO)); } diff --git a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java index e45a707bdc8d..9fe7f3e7a542 100644 --- a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java @@ -22,7 +22,6 @@ import android.os.PersistableBundle; import android.os.Process; import com.android.internal.os.PowerStats; -import com.android.server.power.stats.PowerStatsUidResolver; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.BinaryStatePowerStatsLayout; @@ -45,7 +44,6 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { } private final int mPowerComponentId; - private final PowerStatsUidResolver mUidResolver; private final UsageBasedPowerEstimator mUsageBasedPowerEstimator; private boolean mEnergyConsumerSupported; private int mInitiatingUid = Process.INVALID_UID; @@ -60,18 +58,14 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { private long[] mTmpDeviceStatsArray; private long[] mTmpUidStatsArray; - BinaryStatePowerStatsProcessor(int powerComponentId, - PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) { - this(powerComponentId, uidResolver, averagePowerMilliAmp, - new BinaryStatePowerStatsLayout()); + BinaryStatePowerStatsProcessor(int powerComponentId, double averagePowerMilliAmp) { + this(powerComponentId, averagePowerMilliAmp, new BinaryStatePowerStatsLayout()); } - BinaryStatePowerStatsProcessor(int powerComponentId, - PowerStatsUidResolver uidResolver, double averagePowerMilliAmp, + BinaryStatePowerStatsProcessor(int powerComponentId, double averagePowerMilliAmp, BinaryStatePowerStatsLayout statsLayout) { mPowerComponentId = powerComponentId; mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp); - mUidResolver = uidResolver; mStatsLayout = statsLayout; } @@ -115,13 +109,13 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor { if (state == STATE_ON) { if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE | BatteryStats.HistoryItem.EVENT_FLAG_START)) { - mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid); + mInitiatingUid = item.eventTag.uid; } } else { if (mInitiatingUid == Process.INVALID_UID) { if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE | BatteryStats.HistoryItem.EVENT_FLAG_FINISH)) { - mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid); + mInitiatingUid = item.eventTag.uid; } } recordUsageDuration(mPowerStats, mInitiatingUid, item.time); diff --git a/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java index 830906167ee2..46e060011d92 100644 --- a/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java @@ -20,12 +20,10 @@ import android.os.BatteryConsumer; import android.os.BatteryStats; import com.android.internal.os.PowerProfile; -import com.android.server.power.stats.PowerStatsUidResolver; class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor { - CameraPowerStatsProcessor(PowerProfile powerProfile, - PowerStatsUidResolver uidResolver) { - super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver, + CameraPowerStatsProcessor(PowerProfile powerProfile) { + super(BatteryConsumer.POWER_COMPONENT_CAMERA, powerProfile.getAveragePower(PowerProfile.POWER_CAMERA)); } diff --git a/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java index b0bef69dfc49..e9c1b8253123 100644 --- a/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java @@ -20,12 +20,10 @@ import android.os.BatteryConsumer; import android.os.BatteryStats; import com.android.internal.os.PowerProfile; -import com.android.server.power.stats.PowerStatsUidResolver; class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor { - FlashlightPowerStatsProcessor(PowerProfile powerProfile, - PowerStatsUidResolver uidResolver) { - super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver, + FlashlightPowerStatsProcessor(PowerProfile powerProfile) { + super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)); } diff --git a/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java index f1e3e90e7099..2476432dc504 100644 --- a/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java @@ -23,7 +23,6 @@ import android.os.Process; import com.android.internal.os.PowerProfile; import com.android.internal.os.PowerStats; -import com.android.server.power.stats.PowerStatsUidResolver; import com.android.server.power.stats.UsageBasedPowerEstimator; import com.android.server.power.stats.format.GnssPowerStatsLayout; @@ -40,10 +39,9 @@ class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor { private final long[] mGnssSignalDurations = new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS]; - GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) { - super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver, - powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON), - sStatsLayout); + GnssPowerStatsProcessor(PowerProfile powerProfile) { + super(BatteryConsumer.POWER_COMPONENT_GNSS, + powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON), sStatsLayout); boolean useSignalLevelEstimators = false; for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) { diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java index 25d50f7a045b..877a06fb8c4f 100644 --- a/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java +++ b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java @@ -32,7 +32,6 @@ import com.android.internal.os.PowerProfile; import com.android.server.power.stats.PowerAttributor; import com.android.server.power.stats.PowerStatsSpan; import com.android.server.power.stats.PowerStatsStore; -import com.android.server.power.stats.PowerStatsUidResolver; import java.util.List; import java.util.function.DoubleSupplier; @@ -45,15 +44,12 @@ public class MultiStatePowerAttributor implements PowerAttributor { private final PowerStatsAggregator mPowerStatsAggregator; private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray(); - // TODO(b/346371828): remove dependency on PowerStatsUidResolver. At the time of power - // attribution isolates UIDs are supposed to be long forgotten. public MultiStatePowerAttributor(Context context, PowerStatsStore powerStatsStore, @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies, - @NonNull DoubleSupplier batteryCapacitySupplier, - @NonNull PowerStatsUidResolver powerStatsUidResolver) { + @NonNull DoubleSupplier batteryCapacitySupplier) { this(powerStatsStore, new PowerStatsAggregator( createAggregatedPowerStatsConfig(context, powerProfile, cpuScalingPolicies, - batteryCapacitySupplier, powerStatsUidResolver))); + batteryCapacitySupplier))); } @VisibleForTesting @@ -69,7 +65,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { private static AggregatedPowerStatsConfig createAggregatedPowerStatsConfig(Context context, PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies, - DoubleSupplier batteryCapacitySupplier, PowerStatsUidResolver powerStatsUidResolver) { + DoubleSupplier batteryCapacitySupplier) { AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BASE) .trackDeviceStates( @@ -157,7 +153,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessorSupplier( - () -> new AudioPowerStatsProcessor(powerProfile, powerStatsUidResolver)); + () -> new AudioPowerStatsProcessor(powerProfile)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO) .trackDeviceStates( @@ -168,7 +164,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessorSupplier( - () -> new VideoPowerStatsProcessor(powerProfile, powerStatsUidResolver)); + () -> new VideoPowerStatsProcessor(powerProfile)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT) .trackDeviceStates( @@ -179,8 +175,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessorSupplier( - () -> new FlashlightPowerStatsProcessor(powerProfile, - powerStatsUidResolver)); + () -> new FlashlightPowerStatsProcessor(powerProfile)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA) .trackDeviceStates( @@ -191,7 +186,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessorSupplier( - () -> new CameraPowerStatsProcessor(powerProfile, powerStatsUidResolver)); + () -> new CameraPowerStatsProcessor(powerProfile)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS) .trackDeviceStates( @@ -202,7 +197,7 @@ public class MultiStatePowerAttributor implements PowerAttributor { AggregatedPowerStatsConfig.STATE_SCREEN, AggregatedPowerStatsConfig.STATE_PROCESS_STATE) .setProcessorSupplier( - () -> new GnssPowerStatsProcessor(powerProfile, powerStatsUidResolver)); + () -> new GnssPowerStatsProcessor(powerProfile)); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS) .trackDeviceStates( diff --git a/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java index a6c380725fa5..5514ca7763f9 100644 --- a/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java +++ b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java @@ -20,11 +20,10 @@ import android.os.BatteryConsumer; import android.os.BatteryStats; import com.android.internal.os.PowerProfile; -import com.android.server.power.stats.PowerStatsUidResolver; class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor { - VideoPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) { - super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver, + VideoPowerStatsProcessor(PowerProfile powerProfile) { + super(BatteryConsumer.POWER_COMPONENT_VIDEO, powerProfile.getAveragePower(PowerProfile.POWER_VIDEO)); } diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java new file mode 100644 index 000000000000..0079818e733b --- /dev/null +++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java @@ -0,0 +1,142 @@ +/* + * 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.security.forensic; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.security.forensic.ForensicEvent; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.ServiceThread; + +import java.util.ArrayList; +import java.util.List; + +public class DataAggregator { + private static final String TAG = "Forensic DataAggregator"; + private static final int MSG_SINGLE_DATA = 0; + private static final int MSG_BATCH_DATA = 1; + private static final int MSG_DISABLE = 2; + + private static final int STORED_EVENTS_SIZE_LIMIT = 1024; + private final ForensicService mForensicService; + private final ArrayList<DataSource> mDataSources; + + private List<ForensicEvent> mStoredEvents = new ArrayList<>(); + private ServiceThread mHandlerThread; + private Handler mHandler; + public DataAggregator(ForensicService forensicService) { + mForensicService = forensicService; + mDataSources = new ArrayList<DataSource>(); + } + + @VisibleForTesting + void setHandler(Looper looper, ServiceThread serviceThread) { + mHandlerThread = serviceThread; + mHandler = new EventHandler(looper, this); + } + + /** + * Initialize DataSources + * @return Whether the initialization succeeds. + */ + // TODO: Add the corresponding data sources + public boolean initialize() { + return true; + } + + /** + * Enable the data collection of all DataSources. + */ + public void enable() { + mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND, + /* allowIo */ false); + mHandlerThread.start(); + mHandler = new EventHandler(mHandlerThread.getLooper(), this); + for (DataSource ds : mDataSources) { + ds.enable(); + } + } + + /** + * DataSource calls it to transmit a single event. + */ + public void addSingleData(ForensicEvent event) { + mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget(); + } + + /** + * DataSource calls it to transmit list of events. + */ + public void addBatchData(List<ForensicEvent> events) { + mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget(); + } + + /** + * Disable the data collection of all DataSources. + */ + public void disable() { + mHandler.obtainMessage(MSG_DISABLE).sendToTarget(); + } + + private void onNewSingleData(ForensicEvent event) { + if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) { + mStoredEvents.add(event); + } else { + mForensicService.addNewData(mStoredEvents); + mStoredEvents = new ArrayList<>(); + } + } + + private void onNewBatchData(List<ForensicEvent> events) { + mForensicService.addNewData(events); + } + + private void onDisable() { + for (DataSource ds : mDataSources) { + ds.disable(); + } + mHandlerThread.quitSafely(); + mHandlerThread = null; + } + + private static class EventHandler extends Handler { + private final DataAggregator mDataAggregator; + EventHandler(Looper looper, DataAggregator dataAggregator) { + super(looper); + mDataAggregator = dataAggregator; + } + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SINGLE_DATA: + mDataAggregator.onNewSingleData((ForensicEvent) msg.obj); + break; + case MSG_BATCH_DATA: + mDataAggregator.onNewBatchData((List<ForensicEvent>) msg.obj); + break; + case MSG_DISABLE: + mDataAggregator.onDisable(); + break; + default: + Slog.w(TAG, "Unknown message: " + msg.what); + } + } + } +} diff --git a/services/core/java/com/android/server/security/forensic/DataSource.java b/services/core/java/com/android/server/security/forensic/DataSource.java new file mode 100644 index 000000000000..da7ee210281b --- /dev/null +++ b/services/core/java/com/android/server/security/forensic/DataSource.java @@ -0,0 +1,29 @@ +/* + * 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.security.forensic; + +public interface DataSource { + /** + * Enable the data collection. + */ + void enable(); + + /** + * Disable the data collection. + */ + void disable(); +} diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java index 20c648eb61c2..53b07c04139c 100644 --- a/services/core/java/com/android/server/security/forensic/ForensicService.java +++ b/services/core/java/com/android/server/security/forensic/ForensicService.java @@ -22,6 +22,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.security.forensic.ForensicEvent; import android.security.forensic.IForensicService; import android.security.forensic.IForensicServiceCommandCallback; import android.security.forensic.IForensicServiceStateCallback; @@ -32,6 +33,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import java.util.ArrayList; +import java.util.List; /** * @hide @@ -64,6 +66,7 @@ public class ForensicService extends SystemService { private final Context mContext; private final Handler mHandler; private final BackupTransportConnection mBackupTransportConnection; + private final DataAggregator mDataAggregator; private final BinderService mBinderService; private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>(); @@ -79,6 +82,7 @@ public class ForensicService extends SystemService { mContext = injector.getContext(); mHandler = new EventHandler(injector.getLooper(), this); mBackupTransportConnection = injector.getBackupTransportConnection(); + mDataAggregator = injector.getDataAggregator(this); mBinderService = new BinderService(this); } @@ -167,6 +171,9 @@ public class ForensicService extends SystemService { Slog.e(TAG, "RemoteException", e); } break; + case MSG_BACKUP: + mService.backup((List<ForensicEvent>) msg.obj); + break; default: Slog.w(TAG, "Unknown message: " + msg.what); } @@ -192,6 +199,10 @@ public class ForensicService extends SystemService { private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException { switch (mState) { case STATE_INVISIBLE: + if (!mDataAggregator.initialize()) { + callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE); + break; + } mState = STATE_VISIBLE; notifyStateMonitors(); callback.onSuccess(); @@ -227,6 +238,7 @@ public class ForensicService extends SystemService { callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE); break; } + mDataAggregator.enable(); mState = STATE_ENABLED; notifyStateMonitors(); callback.onSuccess(); @@ -243,6 +255,7 @@ public class ForensicService extends SystemService { switch (mState) { case STATE_ENABLED: mBackupTransportConnection.release(); + mDataAggregator.disable(); mState = STATE_VISIBLE; notifyStateMonitors(); callback.onSuccess(); @@ -255,6 +268,17 @@ public class ForensicService extends SystemService { } } + /** + * Add a list of ForensicEvent. + */ + public void addNewData(List<ForensicEvent> events) { + mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget(); + } + + private void backup(List<ForensicEvent> events) { + mBackupTransportConnection.addData(events); + } + @Override public void onStart() { try { @@ -275,6 +299,8 @@ public class ForensicService extends SystemService { Looper getLooper(); BackupTransportConnection getBackupTransportConnection(); + + DataAggregator getDataAggregator(ForensicService forensicService); } private static final class InjectorImpl implements Injector { @@ -303,6 +329,11 @@ public class ForensicService extends SystemService { public BackupTransportConnection getBackupTransportConnection() { return new BackupTransportConnection(mContext); } + + @Override + public DataAggregator getDataAggregator(ForensicService forensicService) { + return new DataAggregator(forensicService); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 054f9318dfce..25dfbd77642d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1439,7 +1439,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } boolean scheduleTopResumedActivityChanged(boolean onTop) { - if (!attachedToProcess()) { + if (!attachedToProcess() || isState(DESTROYING, DESTROYED)) { ProtoLog.w(WM_DEBUG_STATES, "Can't report activity position update - client not running, " + "activityRecord=%s", this); @@ -4916,7 +4916,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final ActivityLifecycleItem lifecycleItem = getLifecycleItemForCurrentStateForResult(); try { if (lifecycleItem != null) { - mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( + mAtmService.getLifecycleManager().scheduleTransactionItems( app.getThread(), activityResultItem, lifecycleItem); } else { Slog.w(TAG, "Unable to get the lifecycle item for state " + mState @@ -8180,7 +8180,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @ActivityInfo.ScreenOrientation protected int getOverrideOrientation() { int candidateOrientation = super.getOverrideOrientation(); - if (ActivityInfo.isFixedOrientation(candidateOrientation) && isUniversalResizeable()) { + if (candidateOrientation != ActivityInfo.SCREEN_ORIENTATION_LOCKED + && ActivityInfo.isFixedOrientation(candidateOrientation) + && isUniversalResizeable()) { candidateOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } return mAppCompatController.getOrientationPolicy() @@ -9629,7 +9631,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { lifecycleItem = new PauseActivityItem(token); } - mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( + mAtmService.getLifecycleManager().scheduleTransactionItems( app.getThread(), callbackItem, lifecycleItem); startRelaunching(); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java index 5243a1069bed..ed8b6890b2d3 100644 --- a/services/core/java/com/android/server/wm/ActivityRefresher.java +++ b/services/core/java/com/android/server/wm/ActivityRefresher.java @@ -89,7 +89,7 @@ class ActivityRefresher { final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); try { - activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( + activity.mAtmService.getLifecycleManager().scheduleTransactionItems( activity.app.getThread(), refreshCallbackItem, resumeActivityItem); mHandler.postDelayed(() -> { synchronized (mWmService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 2c4179fb6d88..2070c91eaccd 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1275,8 +1275,8 @@ class ActivityStarter { "Creator PermissionPolicyService.checkStartActivity Caused abortion.", intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage); } - intent.removeCreatorTokenInfo(); } + intent.removeCreatorToken(); // Merge the two options bundles, while realCallerOptions takes precedence. ActivityOptions checkedOptions = options != null @@ -1757,6 +1757,13 @@ class ActivityStarter { startedActivityRootTask.setAlwaysOnTop(true); } + if (isIndependentLaunch && !mDoResume && avoidMoveToFront() && !mTransientLaunch + && !started.shouldBeVisible(true /* ignoringKeyguard */)) { + Slog.i(TAG, "Abort " + transition + " of invisible launch " + started); + transition.abort(); + return startedActivityRootTask; + } + // If there is no state change (e.g. a resumed activity is reparented to top of // another display) to trigger a visibility/configuration checking, we have to // update the configuration for changing to different display. @@ -2367,6 +2374,9 @@ class ActivityStarter { return START_SUCCESS; } + if (mMovedToTopActivity != null) { + targetTaskTop = mMovedToTopActivity; + } // The reusedActivity could be finishing, for example of starting an activity with // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, use the top running activity in the // task instead. @@ -2605,7 +2615,7 @@ class ActivityStarter { mInTaskFragment = null; mAddingToTaskFragment = null; mAddingToTask = false; - + mMovedToTopActivity = null; mSourceRootTask = null; mTargetRootTask = null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 111e74e9b590..25a1ea9579e7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1228,7 +1228,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { - mAmInternal.addCreatorToken(intent, callingPackage); return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 567eca2f639a..fa678e2d3176 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -242,6 +242,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { static { ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE, Manifest.permission.CAMERA); + ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_MOTION_PHOTO_CAPTURE, + Manifest.permission.CAMERA); ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE, Manifest.permission.CAMERA); ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL, @@ -964,11 +966,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // transaction. mService.getLifecycleManager().dispatchPendingTransaction(proc.getThread()); } - mService.getLifecycleManager().scheduleTransactionAndLifecycleItems( - proc.getThread(), launchActivityItem, lifecycleItem, + mService.getLifecycleManager().scheduleTransactionItems( + proc.getThread(), // Immediately dispatch the transaction, so that if it fails, the server can // restart the process and retry now. - true /* shouldDispatchImmediately */); + true /* shouldDispatchImmediately */, + launchActivityItem, lifecycleItem); if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) { // If the seq is increased, there should be something changed (e.g. registered @@ -2280,6 +2283,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * sent to the new top resumed activity. */ ActivityRecord updateTopResumedActivityIfNeeded(String reason) { + if (!readyToResume()) { + return mTopResumedActivity; + } final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { @@ -2340,8 +2346,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { && mTopResumedActivity.scheduleTopResumedActivityChanged(false /* onTop */)) { scheduleTopResumedStateLossTimeout(mTopResumedActivity); mTopResumedActivityWaitingForPrev = true; - mTopResumedActivity = null; } + mTopResumedActivity = null; } /** Schedule top resumed state change if previous top activity already reported back. */ diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java index afc6506f9091..4e390df14131 100644 --- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java @@ -22,6 +22,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxInnerBounds; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxOuterBounds; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxPosition; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +35,7 @@ import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.LetterboxDetails; import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType; +import com.android.window.flags.Flags; import java.io.PrintWriter; @@ -43,7 +47,7 @@ class AppCompatLetterboxPolicy { @NonNull private final ActivityRecord mActivityRecord; @NonNull - private final LetterboxPolicyState mLetterboxPolicyState; + private final AppCompatLetterboxPolicyState mLetterboxPolicyState; @NonNull private final AppCompatRoundedCorners mAppCompatRoundedCorners; @NonNull @@ -54,7 +58,8 @@ class AppCompatLetterboxPolicy { AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord, @NonNull AppCompatConfiguration appCompatConfiguration) { mActivityRecord = activityRecord; - mLetterboxPolicyState = new LetterboxPolicyState(); + mLetterboxPolicyState = Flags.appCompatRefactoring() ? new ShellLetterboxPolicyState() + : new LegacyLetterboxPolicyState(); // TODO (b/358334569) Improve cutout logic dependency on app compat. mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord, this::isLetterboxedNotForDisplayCutout); @@ -88,7 +93,24 @@ class AppCompatLetterboxPolicy { @Nullable LetterboxDetails getLetterboxDetails() { - return mLetterboxPolicyState.getLetterboxDetails(); + final WindowState w = mActivityRecord.findMainWindow(); + if (!isRunning() || w == null || w.isLetterboxedForDisplayCutout()) { + return null; + } + final Rect letterboxInnerBounds = new Rect(); + final Rect letterboxOuterBounds = new Rect(); + mLetterboxPolicyState.getLetterboxInnerBounds(letterboxInnerBounds); + mLetterboxPolicyState.getLetterboxOuterBounds(letterboxOuterBounds); + + if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) { + return null; + } + + return new LetterboxDetails( + letterboxInnerBounds, + letterboxOuterBounds, + w.mAttrs.insetsFlags.appearance + ); } /** @@ -99,6 +121,13 @@ class AppCompatLetterboxPolicy { return mLetterboxPolicyState.isFullyTransparentBarAllowed(rect); } + /** + * Updates the letterbox surfaces in case this is needed. + * + * @param winHint The WindowState for the letterboxed Activity. + * @param t The current Transaction. + * @param inputT The pending transaction used for the input surface. + */ void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction inputT) { @@ -232,12 +261,17 @@ class AppCompatLetterboxPolicy { || w.mAnimatingExit; } - private class LetterboxPolicyState { + /** + * Existing {@link AppCompatLetterboxPolicyState} implementation. + * TODO(b/375339716): Clean code for legacy implementation. + */ + private class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { @Nullable private Letterbox mLetterbox; - void layoutLetterboxIfNeeded(@NonNull WindowState w) { + @Override + public void layoutLetterboxIfNeeded(@NonNull WindowState w) { if (!isRunning()) { final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord .mAppCompatController.getAppCompatLetterboxOverrides(); @@ -252,41 +286,11 @@ class AppCompatLetterboxPolicy { .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame); } final Point letterboxPosition = new Point(); - if (mActivityRecord.isInLetterboxAnimation()) { - // In this case we attach the letterbox to the task instead of the activity. - mActivityRecord.getTask().getPosition(letterboxPosition); - } else { - mActivityRecord.getPosition(letterboxPosition); - } - - // Get the bounds of the "space-to-fill". The transformed bounds have the highest - // priority because the activity is launched in a rotated environment. In multi-window - // mode, the taskFragment-level represents this for both split-screen - // and activity-embedding. In fullscreen-mode, the task container does - // (since the orientation letterbox is also applied to the task). - final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds(); - final Rect spaceToFill = transformedBounds != null - ? transformedBounds - : mActivityRecord.inMultiWindowMode() - ? mActivityRecord.getTaskFragment().getBounds() - : mActivityRecord.getRootTask().getParent().getBounds(); - // In case of translucent activities an option is to use the WindowState#getFrame() of - // the first opaque activity beneath. In some cases (e.g. an opaque activity is using - // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct - // information and in particular it might provide a value for a smaller area making - // the letterbox overlap with the translucent activity's frame. - // If we use WindowState#getFrame() for the translucent activity's letterbox inner - // frame, the letterbox will then be overlapped with the translucent activity's frame. - // Because the surface layer of letterbox is lower than an activity window, this - // won't crop the content, but it may affect other features that rely on values stored - // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher - // For this reason we use ActivityRecord#getBounds() that the translucent activity - // inherits from the first opaque activity beneath and also takes care of the scaling - // in case of activities in size compat mode. - final TransparentPolicy transparentPolicy = - mActivityRecord.mAppCompatController.getTransparentPolicy(); - final Rect innerFrame = - transparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame(); + calculateLetterboxPosition(mActivityRecord, letterboxPosition); + final Rect spaceToFill = new Rect(); + calculateLetterboxOuterBounds(mActivityRecord, spaceToFill); + final Rect innerFrame = new Rect(); + calculateLetterboxInnerBounds(mActivityRecord, w, innerFrame); mLetterbox.layout(spaceToFill, innerFrame, letterboxPosition); if (mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides() .isDoubleTapEvent()) { @@ -299,18 +303,21 @@ class AppCompatLetterboxPolicy { * @return {@code true} if the policy is running and so if the current activity is * letterboxed. */ - boolean isRunning() { + @Override + public boolean isRunning() { return mLetterbox != null; } - void onMovedToDisplay(int displayId) { + @Override + public void onMovedToDisplay(int displayId) { if (isRunning()) { mLetterbox.onMovedToDisplay(displayId); } } /** Cleans up {@link Letterbox} if it exists.*/ - void stop() { + @Override + public void stop() { if (isRunning()) { mLetterbox.destroy(); mLetterbox = null; @@ -319,7 +326,8 @@ class AppCompatLetterboxPolicy { .setLetterboxInnerBoundsSupplier(null); } - void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, + @Override + public void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction inputT) { if (shouldNotLayoutLetterbox(winHint)) { @@ -331,15 +339,17 @@ class AppCompatLetterboxPolicy { } } - void hide() { + @Override + public void hide() { if (isRunning()) { mLetterbox.hide(); } } /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */ + @Override @NonNull - Rect getLetterboxInsets() { + public Rect getLetterboxInsets() { if (isRunning()) { return mLetterbox.getInsets(); } else { @@ -348,7 +358,8 @@ class AppCompatLetterboxPolicy { } /** Gets the inner bounds of letterbox. The bounds will be empty with no letterbox. */ - void getLetterboxInnerBounds(@NonNull Rect outBounds) { + @Override + public void getLetterboxInnerBounds(@NonNull Rect outBounds) { if (isRunning()) { outBounds.set(mLetterbox.getInnerFrame()); final WindowState w = mActivityRecord.findMainWindow(); @@ -361,7 +372,8 @@ class AppCompatLetterboxPolicy { } /** Gets the outer bounds of letterbox. The bounds will be empty with no letterbox. */ - private void getLetterboxOuterBounds(@NonNull Rect outBounds) { + @Override + public void getLetterboxOuterBounds(@NonNull Rect outBounds) { if (isRunning()) { outBounds.set(mLetterbox.getOuterFrame()); } else { @@ -373,39 +385,130 @@ class AppCompatLetterboxPolicy { * @return {@code true} if bar shown within a given rectangle is allowed to be fully * transparent when the current activity is displayed. */ - boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { + @Override + public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { return !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect); } @Nullable - LetterboxDetails getLetterboxDetails() { - final WindowState w = mActivityRecord.findMainWindow(); - if (!isRunning() || w == null || w.isLetterboxedForDisplayCutout()) { - return null; + private SurfaceControl getLetterboxParentSurface() { + if (mActivityRecord.isInLetterboxAnimation()) { + return mActivityRecord.getTask().getSurfaceControl(); + } + return mActivityRecord.getSurfaceControl(); + } + + } + + /** + * {@link AppCompatLetterboxPolicyState} implementation for the letterbox presentation on shell. + */ + private class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { + + private final Rect mInnerBounds = new Rect(); + private final Rect mOuterBounds = new Rect(); + private final Point mLetterboxPosition = new Point(); + private boolean mRunning; + + @Override + public void layoutLetterboxIfNeeded(@NonNull WindowState w) { + mRunning = true; + calculateLetterboxPosition(mActivityRecord, mLetterboxPosition); + calculateLetterboxOuterBounds(mActivityRecord, mOuterBounds); + calculateLetterboxInnerBounds(mActivityRecord, w, mInnerBounds); + mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy() + .setLetterboxInnerBoundsSupplier(() -> mInnerBounds); + } + + @Override + public boolean isRunning() { + return mRunning; + } + + @Override + public void onMovedToDisplay(int displayId) { + // TODO(b/374918469): Handle Display Change for Letterbox in Shell + } + + @Override + public void stop() { + if (!isRunning()) { + return; + } + mRunning = false; + mLetterboxPosition.set(0, 0); + mInnerBounds.setEmpty(); + mOuterBounds.setEmpty(); + mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy() + .setLetterboxInnerBoundsSupplier(null); + } + + @Override + public void hide() { + if (!isRunning()) { + return; } - final Rect letterboxInnerBounds = new Rect(); - final Rect letterboxOuterBounds = new Rect(); - getLetterboxInnerBounds(letterboxInnerBounds); - getLetterboxOuterBounds(letterboxOuterBounds); + mLetterboxPosition.set(0, 0); + mInnerBounds.setEmpty(); + mOuterBounds.setEmpty(); + } - if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) { - return null; + @NonNull + @Override + public Rect getLetterboxInsets() { + if (isRunning()) { + return new Rect( + Math.max(0, mInnerBounds.left - mOuterBounds.left), + Math.max(0, mOuterBounds.top - mInnerBounds.top), + Math.max(0, mOuterBounds.right - mInnerBounds.right), + Math.max(0, mInnerBounds.bottom - mOuterBounds.bottom) + ); } + return new Rect(); + } - return new LetterboxDetails( - letterboxInnerBounds, - letterboxOuterBounds, - w.mAttrs.insetsFlags.appearance - ); + @Override + public void getLetterboxInnerBounds(@NonNull Rect outBounds) { + if (isRunning()) { + outBounds.set(mInnerBounds); + final WindowState w = mActivityRecord.findMainWindow(); + if (w != null) { + AppCompatUtils.adjustBoundsForTaskbar(w, outBounds); + } + } else { + outBounds.setEmpty(); + } } - @Nullable - private SurfaceControl getLetterboxParentSurface() { - if (mActivityRecord.isInLetterboxAnimation()) { - return mActivityRecord.getTask().getSurfaceControl(); + @Override + public void getLetterboxOuterBounds(@NonNull Rect outBounds) { + if (isRunning()) { + outBounds.set(mOuterBounds); + } else { + outBounds.setEmpty(); } - return mActivityRecord.getSurfaceControl(); } + @Override + public void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, + @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction inputT) { + + if (shouldNotLayoutLetterbox(winHint)) { + return; + } + start(winHint); + } + + @Override + public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { + // TODO(b/374921442) Handle Transparent Activities Letterboxing in Shell. + // At the moment Shell handles letterbox with a single surface. This would make + // notIntersectsOrFullyContains() to return false in the existing Letterbox + // implementation. + // Note: Previous implementation is + // !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect); + return !isRunning(); + } } } diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicyState.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicyState.java new file mode 100644 index 000000000000..31ad536c2eb2 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicyState.java @@ -0,0 +1,79 @@ +/* + * 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 android.annotation.NonNull; +import android.graphics.Rect; +import android.view.SurfaceControl; + +/** + * Abstraction for different Letterbox state implementations. + */ +interface AppCompatLetterboxPolicyState { + + /** + * Checks if a relayout is necessary for the letterbox implementations. + * @param w The {@link WindowState} to use for defining Letterbox sizes. + */ + void layoutLetterboxIfNeeded(@NonNull WindowState w); + + /** + * @return {@code true} if the policy is running and so if the current activity is + * letterboxed. + */ + boolean isRunning(); + + /** + * Called when the activity is moved to a new display. + * @param displayId Id for the new display + */ + void onMovedToDisplay(int displayId); + + /** Cleans up {@link Letterbox} if it exists.*/ + void stop(); + + /** Hides the letterbox surfaces implementation. */ + void hide(); + + /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */ + @NonNull + Rect getLetterboxInsets(); + + /** Gets the inner bounds of letterbox. The bounds will be empty with no letterbox. */ + void getLetterboxInnerBounds(@NonNull Rect outBounds); + + /** Gets the outer bounds of letterbox. The bounds will be empty with no letterbox. */ + void getLetterboxOuterBounds(@NonNull Rect outBounds); + + /** + * Updates the letterbox surfaces in case this is needed. + * + * @param winHint The WindowState for the letterboxed Activity. + * @param t The current Transaction. + * @param inputT The pending transaction used for the input surface. + */ + void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, + @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction inputT); + + /** + * @return {@code true} if bar shown within a given rectangle is allowed to be fully + * transparent when the current activity is displayed. + */ + boolean isFullyTransparentBarAllowed(@NonNull Rect rect); + +} diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java b/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java new file mode 100644 index 000000000000..79b3a55d0463 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppCompatLetterboxUtils.java @@ -0,0 +1,107 @@ +/* + * 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 android.annotation.NonNull; +import android.graphics.Point; +import android.graphics.Rect; + +/** + * Some utility methods used by different Letterbox implementations. + */ +class AppCompatLetterboxUtils { + /** + * Provides the position of the top left letterbox area in the display coordinate system. + * + * @param activity The Letterboxed activity. + * @param outLetterboxPosition InOut parameter that will contain the desired letterbox position. + */ + static void calculateLetterboxPosition(@NonNull ActivityRecord activity, + @NonNull Point outLetterboxPosition) { + if (!activity.mAppCompatController.getAppCompatLetterboxPolicy().isRunning()) { + outLetterboxPosition.set(0, 0); + return; + } + if (activity.isInLetterboxAnimation()) { + // In this case we attach the letterbox to the task instead of the activity. + activity.getTask().getPosition(outLetterboxPosition); + } else { + activity.getPosition(outLetterboxPosition); + } + } + + /** + * Provides all the available space, in display coordinate, to fill with the letterboxed + * activity and the letterbox areas. + * + * @param activity The Letterboxed activity. + * @param outOuterBounds InOut parameter that will contain the outer bounds for the letterboxed + * activity. + */ + static void calculateLetterboxOuterBounds(@NonNull ActivityRecord activity, + @NonNull Rect outOuterBounds) { + if (!activity.mAppCompatController.getAppCompatLetterboxPolicy().isRunning()) { + outOuterBounds.setEmpty(); + return; + } + // Get the bounds of the "space-to-fill". The transformed bounds have the highest + // priority because the activity is launched in a rotated environment. In multi-window + // mode, the taskFragment-level represents this for both split-screen + // and activity-embedding. In fullscreen-mode, the task container does + // (since the orientation letterbox is also applied to the task). + final Rect transformedBounds = + activity.getFixedRotationTransformDisplayBounds(); + outOuterBounds.set(transformedBounds != null + ? transformedBounds + : activity.inMultiWindowMode() + ? activity.getTaskFragment().getBounds() + : activity.getRootTask().getParent().getBounds()); + } + + /** + * Provides the inner bounds for the letterboxed activity in display coordinates. This is the + * space the letterboxed activity will use. + * + * @param activity The Letterboxed activity. + * @param outInnerBounds InOut parameter that will contain the inner bounds for the letterboxed + * activity. + */ + static void calculateLetterboxInnerBounds(@NonNull ActivityRecord activity, + @NonNull WindowState window, @NonNull Rect outInnerBounds) { + if (!activity.mAppCompatController.getAppCompatLetterboxPolicy().isRunning()) { + outInnerBounds.setEmpty(); + return; + } + // In case of translucent activities an option is to use the WindowState#getFrame() of + // the first opaque activity beneath. In some cases (e.g. an opaque activity is using + // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct + // information and in particular it might provide a value for a smaller area making + // the letterbox overlap with the translucent activity's frame. + // If we use WindowState#getFrame() for the translucent activity's letterbox inner + // frame, the letterbox will then be overlapped with the translucent activity's frame. + // Because the surface layer of letterbox is lower than an activity window, this + // won't crop the content, but it may affect other features that rely on values stored + // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher + // For this reason we use ActivityRecord#getBounds() that the translucent activity + // inherits from the first opaque activity beneath and also takes care of the scaling + // in case of activities in size compat mode. + final TransparentPolicy transparentPolicy = + activity.mAppCompatController.getTransparentPolicy(); + outInnerBounds.set( + transparentPolicy.isRunning() ? activity.getBounds() : window.getFrame()); + } +} diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 0acc6610a218..d291d99f2a7a 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -19,7 +19,6 @@ package com.android.server.wm; import android.annotation.NonNull; import android.app.IApplicationThread; import android.app.compat.CompatChanges; -import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.LaunchActivityItem; @@ -97,31 +96,34 @@ class ClientLifecycleManager { } /** - * Schedules a single transaction item, either a callback or a lifecycle request, delivery to - * client application. + * Schedules a transaction with the given item, delivery to client application. + * * @throws RemoteException * @see ClientTransactionItem */ void scheduleTransactionItem(@NonNull IApplicationThread client, - @NonNull ClientTransactionItem transactionItem) throws RemoteException { + @NonNull ClientTransactionItem item) throws RemoteException { // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending // transactions at once. final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); - clientTransaction.addTransactionItem(transactionItem); + clientTransaction.addTransactionItem(item); - onClientTransactionItemScheduled(clientTransaction, - false /* shouldDispatchImmediately */); + onClientTransactionItemScheduled(clientTransaction, false /* shouldDispatchImmediately */); } - void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, - @NonNull ClientTransactionItem transactionItem, - @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException { - scheduleTransactionAndLifecycleItems(client, transactionItem, lifecycleItem, - false /* shouldDispatchImmediately */); + /** + * Schedules a transaction with the given items, delivery to client application. + * + * @throws RemoteException + * @see ClientTransactionItem + */ + void scheduleTransactionItems(@NonNull IApplicationThread client, + @NonNull ClientTransactionItem... items) throws RemoteException { + scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items); } /** - * Schedules a single transaction item with a lifecycle request, delivery to client application. + * Schedules a transaction with the given items, delivery to client application. * * @param shouldDispatchImmediately whether or not to dispatch the transaction immediately. This * should only be {@code true} when it is important to know the @@ -133,15 +135,17 @@ class ClientLifecycleManager { * @throws RemoteException * @see ClientTransactionItem */ - void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, - @NonNull ClientTransactionItem transactionItem, - @NonNull ActivityLifecycleItem lifecycleItem, - boolean shouldDispatchImmediately) throws RemoteException { + void scheduleTransactionItems(@NonNull IApplicationThread client, + boolean shouldDispatchImmediately, + @NonNull ClientTransactionItem... items) throws RemoteException { // Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending // transactions at once. final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); - clientTransaction.addTransactionItem(transactionItem); - clientTransaction.addTransactionItem(lifecycleItem); + + final int size = items.length; + for (int i = 0; i < size; i++) { + clientTransaction.addTransactionItem(items[i]); + } onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 04a625b4a237..5b2fecd584b0 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -65,10 +65,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM; @@ -109,7 +105,6 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.view.DisplayInfo; -import android.view.Gravity; import android.view.InsetsFlags; import android.view.InsetsFrameProvider; import android.view.InsetsSource; @@ -142,7 +137,6 @@ import com.android.internal.view.AppearanceRegion; import com.android.internal.widget.PointerLocationView; import com.android.server.LocalServices; import com.android.server.UiThread; -import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition; import com.android.server.policy.WindowManagerPolicy.ScreenOnListener; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; import com.android.server.statusbar.StatusBarManagerInternal; @@ -263,8 +257,7 @@ public class DisplayPolicy { private WindowState mStatusBar = null; private volatile WindowState mNotificationShade; private WindowState mNavigationBar = null; - @NavigationBarPosition - private int mNavigationBarPosition = NAV_BAR_BOTTOM; + private boolean mHasBottomNavigationBar = true; private final ArraySet<WindowState> mInsetsSourceWindowsExceptIme = new ArraySet<>(); @@ -1255,8 +1248,7 @@ public class DisplayPolicy { throw new IllegalArgumentException("IME insets must be provided by a window."); } - if (!ENABLE_HIDE_IME_CAPTION_BAR && mNavigationBar != null - && navigationBarPosition(displayFrames.mRotation) == NAV_BAR_BOTTOM) { + if (!ENABLE_HIDE_IME_CAPTION_BAR && mNavigationBar != null && mHasBottomNavigationBar) { // In gesture navigation, nav bar frame is larger than frame to calculate insets. // IME should not provide frame which is smaller than the nav bar frame. Otherwise, // nav bar might be overlapped with the content of the client when IME is shown. @@ -1469,10 +1461,9 @@ public class DisplayPolicy { public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) { if (attrs.type == TYPE_NAVIGATION_BAR) { - // Keep mNavigationBarPosition updated to make sure the transient detection and bar - // color control is working correctly. - final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames; - mNavigationBarPosition = navigationBarPosition(displayFrames.mRotation); + // Keep mHasBottomNavigationBar updated to make sure the bar color control is working + // correctly. + mHasBottomNavigationBar = hasBottomNavigationBar(); } final boolean affectsSystemUi = win.canAffectSystemUiFlags(); if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); @@ -2230,20 +2221,11 @@ public class DisplayPolicy { mDisplayContent.mDisplayUpdater.onDisplaySwitching(true); } - @NavigationBarPosition - int navigationBarPosition(int displayRotation) { - if (mNavigationBar != null) { - final int gravity = mNavigationBar.mAttrs.forRotation(displayRotation).gravity; - switch (gravity) { - case Gravity.LEFT: - return NAV_BAR_LEFT; - case Gravity.RIGHT: - return NAV_BAR_RIGHT; - default: - return NAV_BAR_BOTTOM; - } - } - return NAV_BAR_INVALID; + boolean hasBottomNavigationBar() { + Insets navBarInsets = mDisplayContent.getInsetsStateController().getRawInsetsState() + .calculateInsets(mDisplayContent.mDisplayFrames.mUnrestricted, + Type.navigationBars(), true /* ignoreVisibilities */); + return navBarInsets.bottom > 0; } /** @@ -2405,7 +2387,7 @@ public class DisplayPolicy { return; } final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate, - mDisplayContent.mInputMethodWindow, mNavigationBarPosition); + mDisplayContent.mInputMethodWindow, mHasBottomNavigationBar); final boolean isNavbarColorManagedByIme = navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance, @@ -2467,12 +2449,12 @@ public class DisplayPolicy { @VisibleForTesting @Nullable static WindowState chooseNavigationColorWindowLw(WindowState candidate, WindowState imeWindow, - @NavigationBarPosition int navBarPosition) { + boolean hasBottomNavigationBar) { // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME // window can be navigation color window. final boolean imeWindowCanNavColorWindow = imeWindow != null && imeWindow.isVisible() - && navBarPosition == NAV_BAR_BOTTOM + && hasBottomNavigationBar && (imeWindow.mAttrs.flags & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; if (!imeWindowCanNavColorWindow) { @@ -2689,7 +2671,7 @@ public class DisplayPolicy { final WindowState navBackgroundWin = chooseNavigationBackgroundWindow( mNavBarBackgroundWindowCandidate, mDisplayContent.mInputMethodWindow, - mNavigationBarPosition); + mHasBottomNavigationBar); final boolean drawBackground = navBackgroundWin != null // There is no app window showing underneath nav bar. (e.g., The screen is locked.) // Let system windows (ex: notification shade) draw nav bar background. @@ -2727,8 +2709,8 @@ public class DisplayPolicy { @VisibleForTesting @Nullable static WindowState chooseNavigationBackgroundWindow(WindowState candidate, - WindowState imeWindow, @NavigationBarPosition int navBarPosition) { - if (imeWindow != null && imeWindow.isVisible() && navBarPosition == NAV_BAR_BOTTOM + WindowState imeWindow, boolean hasBottomNavigationBar) { + if (imeWindow != null && imeWindow.isVisible() && hasBottomNavigationBar && drawsBarBackground(imeWindow)) { return imeWindow; } @@ -2906,8 +2888,8 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar); pw.print(prefix); pw.print("mNavBarOpacityMode="); pw.println(mNavBarOpacityMode); pw.print(prefix); pw.print("mNavigationBarCanMove="); pw.println(mNavigationBarCanMove); - pw.print(prefix); pw.print("mNavigationBarPosition="); - pw.println(mNavigationBarPosition); + pw.print(prefix); pw.print("mHasBottomNavigationBar="); + pw.println(mHasBottomNavigationBar); } if (mLeftGestureHost != null) { pw.print(prefix); pw.print("mLeftGestureHost="); pw.println(mLeftGestureHost); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index a5085fc3147c..ea0b02c58974 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -191,7 +191,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.power.stats-V1-ndk", "android.hardware.thermal@1.0", - "android.hardware.thermal-V2-ndk", + "android.hardware.thermal-V3-ndk", "android.hardware.tv.input@1.0", "android.hardware.tv.input-V2-ndk", "android.hardware.vibrator-V3-ndk", diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 248ed1a58b75..416e60f06c06 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -3056,6 +3056,12 @@ static void nativeSetMouseSwapPrimaryButtonEnabled(JNIEnv* env, jobject nativeIm im->setMouseSwapPrimaryButtonEnabled(enabled); } +static jboolean nativeSetKernelWakeEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId, + jboolean enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + return im->getInputManager()->getReader().setKernelWakeEnabled(deviceId, enabled); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gInputManagerMethods[] = { @@ -3172,6 +3178,7 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeSetAccessibilityStickyKeysEnabled}, {"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive}, {"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId}, + {"setKernelWakeEnabled", "(IZ)Z", (void*)nativeSetKernelWakeEnabled}, }; #define FIND_CLASS(var, className) \ diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml index 3b81f0a6191e..4c1ac39a5da0 100644 --- a/services/core/lint-baseline.xml +++ b/services/core/lint-baseline.xml @@ -178,4 +178,675 @@ column="51"/> </issue> -</issues>
\ No newline at end of file + <issue + id="MissingPermissionAnnotation" + message="onShellCommand should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java" + line="128" + column="5"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="monitorState should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java" + line="95" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="makeVisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java" + line="100" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="makeInvisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java" + line="105" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="enable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java" + line="110" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="disable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java" + line="115" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="getTimeoutTime should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="430" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="extendTimeRemaining should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="443" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="setVerificationPolicy should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="456" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="reportVerificationIncomplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="470" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="reportVerificationComplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="486" + column="9"/> + </issue> + + <issue + id="MissingPermissionAnnotation" + message="reportVerificationCompleteWithExtensionResponse should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced." + errorLine1=" @Override" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java" + line="492" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityClientController permission check can be converted to @EnforcePermission annotation" + errorLine1=" mService.mAmInternal.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java" + line="592" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityClientController permission check can be converted to @EnforcePermission annotation" + errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java" + line="1636" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityClientController permission check can be converted to @EnforcePermission annotation" + errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java" + line="1654" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="1820" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="1875" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="1980" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="2116" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="2144" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="2206" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="2228" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="2371" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveRootTaskToDisplay()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3103" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3157" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3640" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3683" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3701" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3904" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="3978" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4000" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4029" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4057" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4074" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4136" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4147" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4198" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4215" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="4280" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="5836" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE," + errorLine2=" ^"> + <location + file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java" + line="5849" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IColorDisplayManager permission check should be converted to @EnforcePermission annotation" + errorLine1=" getContext().enforceCallingOrSelfPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java" + line="2152" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java" + line="779" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java" + line="820" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java" + line="906" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="INotificationManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (PERMISSION_GRANTED" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java" + line="6691" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="INotificationManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (PERMISSION_GRANTED" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java" + line="6712" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IOnDeviceIntelligenceManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mContext.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java" + line="237" + column="17"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IPackageInstaller permission check should be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java" + line="1881" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IPackageInstaller permission check should be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java" + line="1892" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IPackageManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY," + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java" + line="5798" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IPackageManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mContext.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java" + line="6266" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="1406" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="1427" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="1734" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="2509" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="2564" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="ITvInputManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java" + line="2932" + column="13"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java" + line="366" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation" + errorLine1=" mAmInternal.enforceCallingPermission(" + errorLine2=" ^"> + <location + file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java" + line="444" + column="9"/> + </issue> + + <issue + id="SimpleManualPermissionEnforcement" + message="IVcnManagementService permission check should be converted to @EnforcePermission annotation" + errorLine1=" mContext.enforceCallingOrSelfPermission(DUMP, TAG);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="frameworks/base/services/core/java/com/android/server/VcnManagementService.java" + line="1329" + column="9"/> + </issue> + +</issues> diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 20c69ac93f63..6eac826f282c 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -955,6 +955,7 @@ <xs:enumeration value="default"/> <xs:enumeration value="idle"/> <xs:enumeration value="doze"/> + <xs:enumeration value="bedtime_wear"/> </xs:restriction> </xs:simpleType> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index a8f18b3d2eee..a29e42cb480d 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -21,6 +21,7 @@ package com.android.server.display.config { public enum AutoBrightnessModeName { method public String getRawName(); enum_constant public static final com.android.server.display.config.AutoBrightnessModeName _default; + enum_constant public static final com.android.server.display.config.AutoBrightnessModeName bedtime_wear; enum_constant public static final com.android.server.display.config.AutoBrightnessModeName doze; enum_constant public static final com.android.server.display.config.AutoBrightnessModeName idle; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e052f94b92ee..ed56382450e9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1504,6 +1504,8 @@ public final class SystemServer implements Dumpable { boolean isTv = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + boolean isAutomotive = RoSystemFeatures.hasFeatureAutomotive(context); + boolean enableVrService = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE); @@ -1760,11 +1762,19 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } - if (android.security.Flags.aapmApi()) { + if (!isWatch && !isTv && !isAutomotive + && android.security.Flags.aapmApi()) { t.traceBegin("StartAdvancedProtectionService"); mSystemServiceManager.startService(AdvancedProtectionService.Lifecycle.class); t.traceEnd(); } + + if (!isWatch && !isTv && !isAutomotive) { + t.traceBegin("StartTradeInModeService"); + mSystemServiceManager.startService(TradeInModeService.class); + t.traceEnd(); + } + } catch (Throwable e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service"); @@ -3030,8 +3040,13 @@ public final class SystemServer implements Dumpable { || context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_RTT)) { t.traceBegin("RangingService"); - mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS, - RANGING_APEX_SERVICE_JAR_PATH); + // TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next. + try { + mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS, + RANGING_APEX_SERVICE_JAR_PATH); + } catch (Throwable e) { + Slog.d(TAG, "service-ranging.jar not found, not starting RangingService"); + } t.traceEnd(); } } @@ -3137,7 +3152,7 @@ public final class SystemServer implements Dumpable { }, WEBVIEW_PREPARATION); } - if (RoSystemFeatures.hasFeatureAutomotive(context)) { + if (isAutomotive) { t.traceBegin("StartCarServiceHelperService"); final SystemService cshs = mSystemServiceManager .startService(CAR_SERVICE_HELPER_SERVICE_CLASS); diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 105147fe212d..42c171bb4351 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.camera2.CameraManager; +import android.hardware.usb.UsbManager; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -67,6 +68,8 @@ public final class ProfcollectForwardingService extends SystemService { private int mUsageSetting; private boolean mUploadEnabled; + private boolean mAdbActive; + private IProfCollectd mIProfcollect; private static ProfcollectForwardingService sSelfService; private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper()); @@ -84,6 +87,14 @@ public final class ProfcollectForwardingService extends SystemService { Log.d(LOG_TAG, "Received broadcast to pack and upload reports"); createAndUploadReport(sSelfService); } + if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) { + boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); + boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false); + if (isADB) { + Log.d(LOG_TAG, "Received broadcast that ADB became " + connected); + mAdbActive = connected; + } + } } }; @@ -106,8 +117,12 @@ public final class ProfcollectForwardingService extends SystemService { mUploadEnabled = context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled); + // TODO: ADB might already be active when our service started. + mAdbActive = false; + final IntentFilter filter = new IntentFilter(); filter.addAction(INTENT_UPLOAD_PROFILES); + filter.addAction(UsbManager.ACTION_USB_STATE); context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } @@ -281,6 +296,9 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } + if (mAdbActive) { + return; + } if (Utils.withFrequency("applaunch_trace_freq", 5)) { Utils.traceSystem(mIProfcollect, "applaunch"); } @@ -303,6 +321,9 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } + if (mAdbActive) { + return; + } if (Utils.withFrequency("dex2oat_trace_freq", 25)) { // Dex2oat could take a while before it starts. Add a short delay before start tracing. Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000); diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp index 682ed91c22dd..8e1013656fe6 100644 --- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp +++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp @@ -28,7 +28,7 @@ java_test_host { "compatibility-tradefed", "compatibility-host-util", ], - data: [ + device_common_data: [ ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 2c785049412a..2bc8af1b913e 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -159,15 +159,16 @@ public class InputMethodServiceTest { // Press home key to hide soft keyboard. Log.i(TAG, "Press home"); - verifyInputViewStatus( - () -> assertThat(mUiDevice.pressHome()).isTrue(), - true /* expected */, - false /* inputViewStarted */); if (Flags.refactorInsetsController()) { + assertThat(mUiDevice.pressHome()).isTrue(); // The IME visibility is only sent at the end of the animation. Therefore, we have to // wait until the visibility was sent to the server and the IME window hidden. eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); } else { + verifyInputViewStatus( + () -> assertThat(mUiDevice.pressHome()).isTrue(), + true /* expected */, + false /* inputViewStarted */); assertThat(mInputMethodService.isInputViewShown()).isFalse(); } } diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index b46a6fffbaa9..6ad40f4d2733 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -44,7 +44,7 @@ java_test_host { "block_device_writer_jar", ], test_suites: ["device-tests"], - data: [ + device_common_data: [ ":PackageManagerTestApex", ":PackageManagerTestApexApp", ":PackageManagerServiceServerTests", @@ -53,7 +53,7 @@ java_test_host { "block_device_writer", "fsverity_multilib", ], - java_resources: [ + device_common_java_resources: [ ":PackageManagerTestOverlayActor", ":PackageManagerTestOverlay", ":PackageManagerTestOverlayTarget", @@ -73,7 +73,7 @@ java_test_host { ], } -genrule { +java_genrule { name: "PackageManagerTestAppVersion3Invalid", tools: [ "soong_zip", diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java index 24617984eaf7..3046d4beb7a3 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -49,6 +50,7 @@ import android.util.Pair; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; @@ -122,6 +124,10 @@ public class VerifierControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + // Mock that the UID of this test becomes the UID of the verifier + when(mSnapshot.getPackageUidInternal(anyString(), anyLong(), anyInt(), anyInt())) + .thenReturn(InstrumentationRegistry.getInstrumentation().getContext() + .getApplicationInfo().uid); when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn( TEST_VERIFIER_COMPONENT_NAME.getPackageName()); when(mInjector.getRemoteService( diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp index 9dacfeabf1ef..d0a2eb873fcb 100644 --- a/services/tests/apexsystemservices/Android.bp +++ b/services/tests/apexsystemservices/Android.bp @@ -27,7 +27,7 @@ java_test_host { name: "ApexSystemServicesTestCases", srcs: ["src/**/*.java"], libs: ["tradefed"], - java_resources: [ + device_common_java_resources: [ ":test_com.android.server", ], static_libs: [ diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 2220f439f6c8..2fd135e5f57a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -18,6 +18,7 @@ package com.android.server.display; import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN; @@ -885,6 +886,34 @@ public final class DisplayDeviceConfigTest { mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( AUTO_BRIGHTNESS_MODE_DOZE, Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA); + + // Wear Bedtime mode curve + assertArrayEquals(new float[]{0.0f, 10.0f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), ZERO_DELTA); + assertArrayEquals(new float[]{0.20f, 0.30f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA); + + assertArrayEquals(new float[]{0.0f, 20.0f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), ZERO_DELTA); + assertArrayEquals(new float[]{0.30f, 0.65f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_NORMAL), SMALL_DELTA); + + assertArrayEquals(new float[]{0.0f, 30.0f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA); + assertArrayEquals(new float[]{0.65f, 0.95f}, + mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels( + AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA); } @Test @@ -1296,6 +1325,51 @@ public final class DisplayDeviceConfigTest { + "</screenBrightnessRampSlowIncreaseIdle>\n"; } + private String getBedTimeModeWearCurveConfig() { + return "<luxToBrightnessMapping>\n" + + "<mode>bedtime_wear</mode>\n" + + "<setting>dim</setting>\n" + + "<map>\n" + + "<point>\n" + + "<first>0</first>\n" + + "<second>0.2</second>\n" + + "</point>\n" + + "<point>\n" + + "<first>10</first>\n" + + "<second>0.3</second>\n" + + "</point>\n" + + "</map>\n" + + "</luxToBrightnessMapping>\n" + + "<luxToBrightnessMapping>\n" + + "<mode>bedtime_wear</mode>\n" + + "<setting>normal</setting>\n" + + "<map>\n" + + "<point>\n" + + "<first>0</first>\n" + + "<second>0.3</second>\n" + + "</point>\n" + + "<point>\n" + + "<first>20</first>\n" + + "<second>0.65</second>\n" + + "</point>\n" + + "</map>\n" + + "</luxToBrightnessMapping>\n" + + "<luxToBrightnessMapping>\n" + + "<mode>bedtime_wear</mode>\n" + + "<setting>bright</setting>\n" + + "<map>\n" + + "<point>\n" + + "<first>0</first>\n" + + "<second>0.65</second>\n" + + "</point>\n" + + "<point>\n" + + "<first>30</first>\n" + + "<second>0.95</second>\n" + + "</point>\n" + + "</map>\n" + + "</luxToBrightnessMapping>\n"; + } + private String getPowerThrottlingConfig() { return "<powerThrottlingConfig >\n" + "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n" @@ -1481,6 +1555,7 @@ public final class DisplayDeviceConfigTest { + "</point>\n" + "</map>\n" + "</luxToBrightnessMapping>\n" + + getBedTimeModeWearCurveConfig() + "<idleStylusTimeoutMillis>1000</idleStylusTimeoutMillis>\n" + "</autoBrightness>\n" + getPowerThrottlingConfig() diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index fdf6b809fa85..27fd1e6187bb 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -18,6 +18,7 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE; import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE; @@ -1290,7 +1291,6 @@ public final class DisplayPowerControllerTest { /* ambientLightHorizonLong= */ anyInt(), eq(lux), eq(nits), - any(BrightnessClamperController.class), any(DisplayManagerFlags.class) ); } @@ -1929,6 +1929,60 @@ public final class DisplayPowerControllerTest { } @Test + public void testSwitchToBedtimeAutoBrightnessMode_wearBedtimeEnabledAndBrightRequest() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mDisplayManagerFlagsMock.isAutoBrightnessModeBedtimeWearEnabled()).thenReturn(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE, + /* value= */ 1); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController) + .switchMode(eq(AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR), /* sendUpdate= */ anyBoolean()); + } + + @Test + public void testNotSwitchToBedtimeAutoBrightnessMode_wearBedtimeDisabled() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mDisplayManagerFlagsMock.isAutoBrightnessModeBedtimeWearEnabled()).thenReturn(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE, + /* value= */ 0); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController) + .switchMode(eq(AUTO_BRIGHTNESS_MODE_DEFAULT), /* sendUpdate= */ anyBoolean()); + } + + @Test + public void testSwitchToDozeAutoBrightnessMode_wearBedtimeEnabledAndDozeRequest() { + when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true); + when(mDisplayManagerFlagsMock.isAutoBrightnessModeBedtimeWearEnabled()).thenReturn(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.Wearable.BEDTIME_MODE, + /* value= */ 1); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController) + .switchMode(eq(AUTO_BRIGHTNESS_MODE_DOZE), /* sendUpdate= */ anyBoolean()); + } + + @Test public void testOnSwitchUserUpdatesBrightness() { int userSerial = 12345; float brightness = 0.65f; @@ -2224,6 +2278,8 @@ public final class DisplayPowerControllerTest { verify(mHolder.animator).animateTo(eq(DEFAULT_DOZE_BRIGHTNESS), /* linearSecondTarget= */ anyFloat(), eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false)); + // This brightness shouldn't be stored in the setting + verify(mHolder.brightnessSetting, never()).setBrightness(DEFAULT_DOZE_BRIGHTNESS); // The display device changes and the default doze brightness changes setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class), @@ -2708,7 +2764,6 @@ public final class DisplayPowerControllerTest { BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { return mAutomaticBrightnessController; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index b3baa5deb4a7..4f875c38b92f 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -121,8 +121,9 @@ public final class DisplayBrightnessControllerTest { int targetDisplayState = Display.STATE_DOZE; when(mDisplayBrightnessStrategySelector.selectStrategy( any(StrategySelectionRequest.class))).thenReturn(displayBrightnessStrategy); - mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState, mock( - DisplayManagerInternal.DisplayOffloadSession.class)); + mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState, + mock(DisplayManagerInternal.DisplayOffloadSession.class), + /* isBedtimeModeWearEnabled= */ false); verify(displayBrightnessStrategy).updateBrightness( eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS, /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false))); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index fe1505162e24..b99a18c8e68c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -199,7 +199,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mDozeBrightnessModeStrategy); } @@ -216,7 +216,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mDozeBrightnessModeStrategy); } @@ -231,7 +231,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mDozeBrightnessModeStrategy); } @@ -255,7 +255,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mDozeBrightnessModeStrategy); } @@ -266,7 +266,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mScreenOffBrightnessModeStrategy); } @@ -279,7 +279,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mOverrideBrightnessStrategy); } @@ -293,7 +293,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mTemporaryBrightnessStrategy); } @@ -308,7 +308,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mBoostBrightnessStrategy); } @@ -323,7 +323,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mInvalidBrightnessStrategy); } @@ -335,7 +335,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mFollowerBrightnessStrategy); } @@ -354,7 +354,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mOffloadBrightnessStrategy); } @@ -379,12 +379,13 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mAutomaticBrightnessStrategy); verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON, true, BrightnessReason.REASON_UNKNOWN, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, - /* useNormalBrightnessForDoze= */ false, 0.1f, false); + /* useNormalBrightnessForDoze= */ false, 0.1f, false, + /* isBedtimeModeWearEnabled= */ false); } @@ -409,7 +410,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_BEING_USED)), + STYLUS_IS_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mAutomaticBrightnessStrategy); } @@ -430,7 +431,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mAutoBrightnessFallbackStrategy); } @@ -449,7 +450,7 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED))); + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false))); } @Test @@ -468,7 +469,7 @@ public final class DisplayBrightnessStrategySelectorTest { assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)), + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)), mFallbackBrightnessStrategy); } @@ -484,12 +485,13 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, 0.1f, false, mDisplayOffloadSession, - STYLUS_IS_NOT_BEING_USED)); + STYLUS_IS_NOT_BEING_USED, /* isBedtimeModeWearEnabled= */ false)); StrategySelectionNotifyRequest strategySelectionNotifyRequest = new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON, mFollowerBrightnessStrategy, 0.1f, - false, false, false); + false, false, false, + /* isBedtimeModeWearEnabled= */ false); for (DisplayBrightnessStrategy displayBrightnessStrategy : mDisplayBrightnessStrategySelector.mDisplayBrightnessStrategies) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java index 2a71af06e0c2..e3893c86a6de 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java @@ -150,7 +150,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(true); @@ -172,7 +173,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(true); @@ -194,7 +196,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ false); + /* isAutoBrightnessEnabled= */ false, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false); @@ -216,7 +219,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false); @@ -238,7 +242,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false); @@ -260,7 +265,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ true, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false); @@ -282,7 +288,8 @@ public class AutoBrightnessFallbackStrategyTest { /* lastUserSetScreenBrightness= */ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* userSetBrightnessChanged= */ false, /* allowAutoBrightnessWhileDozingConfig= */ false, - /* isAutoBrightnessEnabled= */ true); + /* isAutoBrightnessEnabled= */ true, + /* isBedtimeModeWearEnabled= */ false); mAutoBrightnessFallbackStrategy.strategySelectionPostProcessor(ssnr); verify(mScreenOffBrightnessSensorController).setLightSensorEnabled(false); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java index e38654225c29..ae1b01959ced 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java @@ -112,7 +112,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, @@ -139,7 +140,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -167,7 +169,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, useNormalBrightnessForDoze, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -193,7 +196,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, @@ -223,7 +227,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, useNormalBrightnessForDoze, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, @@ -250,7 +255,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -278,7 +284,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, @@ -307,7 +314,8 @@ public class AutomaticBrightnessStrategy2Test { mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index 8a1f86093ecf..65f8ea7bfdfc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -123,7 +123,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, @@ -150,7 +151,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -177,7 +179,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -204,7 +207,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, @@ -233,7 +237,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, @@ -261,7 +266,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness, - userSetBrightnessChanged); + userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -290,7 +296,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, - lastUserSetBrightness, userSetBrightnessChanged); + lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, @@ -319,7 +326,8 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, allowAutoBrightnessWhileDozing, brightnessReason, policy, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness, - userSetBrightnessChanged); + userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, mBrightnessConfiguration, @@ -352,7 +360,8 @@ public class AutomaticBrightnessStrategyTest { when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true); mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController, never()) .switchMode(anyInt(), /* sendUpdate= */ anyBoolean()); @@ -361,7 +370,8 @@ public class AutomaticBrightnessStrategyTest { when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false); mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); verify(mAutomaticBrightnessController).switchMode( AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ false); @@ -375,7 +385,8 @@ public class AutomaticBrightnessStrategyTest { // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is false. mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); // 1st AUTO_BRIGHTNESS_MODE_DOZE verify(mAutomaticBrightnessController).switchMode( AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE, @@ -385,7 +396,8 @@ public class AutomaticBrightnessStrategyTest { // state is ON, policy is DOZE and useNormalBrightnessForDoze is false. mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); // 2nd AUTO_BRIGHTNESS_MODE_DOZE verify(mAutomaticBrightnessController, times(2)).switchMode( AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE, @@ -396,7 +408,8 @@ public class AutomaticBrightnessStrategyTest { // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is true. mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); // 3rd AUTO_BRIGHTNESS_MODE_DOZE verify(mAutomaticBrightnessController, times(3)).switchMode( AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE, @@ -406,11 +419,42 @@ public class AutomaticBrightnessStrategyTest { // state is ON, policy is DOZE and useNormalBrightnessForDoze is true. mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, allowAutoBrightnessWhileDozing, brightnessReason, policy, - useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged); + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ false); // AUTO_BRIGHTNESS_MODE_DEFAULT verify(mAutomaticBrightnessController).switchMode( AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT, /* sendUpdate= */ false); + + // Wear Bedtime autobrightness mode feature disabled. + when(mDisplayManagerFlags.isAutoBrightnessModeBedtimeWearEnabled()).thenReturn(false); + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, + allowAutoBrightnessWhileDozing, brightnessReason, policy, + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ true); + verify(mAutomaticBrightnessController, times(2)).switchMode( + AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT, + /* sendUpdate= */ false); + + // Wear Bedtime autobrightness mode feature enabled. + when(mDisplayManagerFlags.isAutoBrightnessModeBedtimeWearEnabled()).thenReturn(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, + allowAutoBrightnessWhileDozing, brightnessReason, policy, + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ true); + verify(mAutomaticBrightnessController).switchMode( + AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + /* sendUpdate= */ false); + + // Wear bedtime mode enabled, keep bedtime curve even though doze is requested. + mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE, + allowAutoBrightnessWhileDozing, brightnessReason, + DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE, + useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged, + /* isBedtimeModeWearEnabled= */ true); + verify(mAutomaticBrightnessController, times(2)).switchMode( + AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_BEDTIME_WEAR, + /* sendUpdate= */ false); } @Test @@ -429,14 +473,14 @@ public class AutomaticBrightnessStrategyTest { setTemporaryAutoBrightnessAdjustment(temporaryAutoBrightnessAdjustments); mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness, policy, targetDisplayState, - DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, + DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, brightnessConfiguration, autoBrightnessState); verify(mAutomaticBrightnessController).configure(autoBrightnessState, brightnessConfiguration, lastUserSetScreenBrightness, userSetBrightnessChanged, temporaryAutoBrightnessAdjustments, /* userChangedAutoBrightnessAdjustment= */ false, policy, targetDisplayState, - DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, + DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* shouldResetShortTermModel= */ true); assertTrue(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied()); assertFalse(mAutomaticBrightnessStrategy.shouldResetShortTermModel()); @@ -447,7 +491,7 @@ public class AutomaticBrightnessStrategyTest { mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true); mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness, policy, targetDisplayState, - DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, + DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, brightnessConfiguration, autoBrightnessState); assertFalse(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied()); assertTrue(mAutomaticBrightnessStrategy.shouldResetShortTermModel()); @@ -572,7 +616,7 @@ public class AutomaticBrightnessStrategyTest { BrightnessReason.REASON_UNKNOWN, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f, - /* userSetBrightnessChanged= */ false); + /* userSetBrightnessChanged= */ false, /* isBedtimeModeWearEnabled= */ false); when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null)) .thenReturn(Float.NaN); assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessValid()); @@ -589,7 +633,7 @@ public class AutomaticBrightnessStrategyTest { BrightnessReason.REASON_UNKNOWN, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f, - /* userSetBrightnessChanged= */ false); + /* userSetBrightnessChanged= */ false, /* isBedtimeModeWearEnabled= */ false); when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null)) .thenReturn(0.2f); assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessValid()); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 250c2f93e70e..6ab72cd33042 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -167,8 +167,6 @@ public class MockingOomAdjusterTests { private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; private static int sFirstUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10; - private static int sFirstNonUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 20; - private static int sUiTierSize = 10; private Context mContext; private ProcessStateController mProcessStateController; @@ -177,6 +175,9 @@ public class MockingOomAdjusterTests { private ActivityManagerService mService; private OomAdjusterInjector mInjector = new OomAdjusterInjector(); + private int mUiTierSize; + private int mFirstNonUiCachedAdj; + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -247,6 +248,9 @@ public class MockingOomAdjusterTests { mService.mOomAdjuster.mAdjSeq = 10000; mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC); + + mUiTierSize = mService.mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE; + mFirstNonUiCachedAdj = sFirstUiCachedAdj + mUiTierSize; } @SuppressWarnings("GuardedBy") @@ -927,8 +931,8 @@ public class MockingOomAdjusterTests { final int mruIndex = numberOfApps - i - 1; int expectedAdj; if (mService.mConstants.USE_TIERED_CACHED_ADJ) { - expectedAdj = (i < numberOfApps - sUiTierSize) - ? sFirstNonUiCachedAdj : sFirstUiCachedAdj + mruIndex; + expectedAdj = (i < numberOfApps - mUiTierSize) + ? mFirstNonUiCachedAdj : sFirstUiCachedAdj + mruIndex; } else { expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS); if (expectedAdj > CACHED_APP_MAX_ADJ) { @@ -1108,7 +1112,7 @@ public class MockingOomAdjusterTests { updateOomAdj(app); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND); } @@ -1545,7 +1549,7 @@ public class MockingOomAdjusterTests { updateOomAdj(app); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND); } @@ -1563,7 +1567,7 @@ public class MockingOomAdjusterTests { updateOomAdj(app, client); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND); } @@ -1694,7 +1698,7 @@ public class MockingOomAdjusterTests { mProcessStateController.runFollowUpUpdate(); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND, "cch-empty"); // Follow up should not have been called again. @@ -2741,7 +2745,7 @@ public class MockingOomAdjusterTests { final int userOther = 1; // cachedAdj1 and cachedAdj2 will be read if USE_TIERED_CACHED_ADJ is disabled. Otherwise, - // sFirstUiCachedAdj and sFirstNonUiCachedAdj are used instead. + // sFirstUiCachedAdj and mFirstNonUiCachedAdj are used instead. final int cachedAdj1 = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS; final int cachedAdj2 = cachedAdj1 + CACHED_APP_IMPORTANCE_LEVELS * 2; doReturn(userOwner).when(mService.mUserController).getCurrentUserId(); @@ -2789,7 +2793,7 @@ public class MockingOomAdjusterTests { mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstUiCachedAdj : cachedAdj1, SCHED_GROUP_BACKGROUND, "cch-started-ui-services", true); assertProcStates(app2, PROCESS_STATE_SERVICE, - mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj2, + mService.mConstants.USE_TIERED_CACHED_ADJ ? mFirstNonUiCachedAdj : cachedAdj2, SCHED_GROUP_BACKGROUND, "cch-started-services", true); app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT); @@ -2809,7 +2813,7 @@ public class MockingOomAdjusterTests { updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, - mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1, + mService.mConstants.USE_TIERED_CACHED_ADJ ? mFirstNonUiCachedAdj : cachedAdj1, SCHED_GROUP_BACKGROUND, "cch-started-services", true); mProcessStateController.stopService(app.mServices, s); @@ -2831,7 +2835,7 @@ public class MockingOomAdjusterTests { assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, "started-services", false); assertProcStates(app2, PROCESS_STATE_SERVICE, - mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1, + mService.mConstants.USE_TIERED_CACHED_ADJ ? mFirstNonUiCachedAdj : cachedAdj1, SCHED_GROUP_BACKGROUND, "cch-started-services", true); app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT); @@ -2845,7 +2849,7 @@ public class MockingOomAdjusterTests { assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, "started-services", false); assertProcStates(app2, PROCESS_STATE_SERVICE, - mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1, + mService.mConstants.USE_TIERED_CACHED_ADJ ? mFirstNonUiCachedAdj : cachedAdj1, SCHED_GROUP_BACKGROUND, "cch-started-services", true); doReturn(userOther).when(mService.mUserController).getCurrentUserId(); @@ -2853,7 +2857,7 @@ public class MockingOomAdjusterTests { updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, - mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1, + mService.mConstants.USE_TIERED_CACHED_ADJ ? mFirstNonUiCachedAdj : cachedAdj1, SCHED_GROUP_BACKGROUND, "cch-started-services", true); assertProcStates(app2, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, "started-services", false); @@ -3157,7 +3161,7 @@ public class MockingOomAdjusterTests { mProcessStateController.runFollowUpUpdate(); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND, "cch-started-services"); // Follow up should not have been called again. @@ -3192,7 +3196,7 @@ public class MockingOomAdjusterTests { mProcessStateController.runFollowUpUpdate(); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ - ? sFirstNonUiCachedAdj : sFirstCachedAdj; + ? mFirstNonUiCachedAdj : sFirstCachedAdj; assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND, "cch-empty"); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java index aa9d8c6ea713..f1072da4161f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java @@ -18,6 +18,7 @@ package com.android.server.power; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -29,10 +30,16 @@ import android.hardware.thermal.TemperatureType; import android.hardware.thermal.ThrottlingSeverity; import android.os.Binder; import android.os.CoolingDevice; +import android.os.Flags; import android.os.RemoteException; import android.os.Temperature; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -40,16 +47,36 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class ThermalManagerServiceMockingTest { - @Mock private IThermal mAidlHalMock; + @ClassRule + public static final SetFlagsRule.ClassRule mSetFlagsClassRule = new SetFlagsRule.ClassRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule(); + + @Mock + private IThermal mAidlHalMock; private Binder mAidlBinder = new Binder(); private CompletableFuture<Temperature> mTemperatureFuture; - private ThermalManagerService.ThermalHalWrapper.TemperatureChangedCallback mTemperatureCallback; + private CompletableFuture<TemperatureThreshold> mThresholdFuture; + private ThermalManagerService.ThermalHalWrapper.WrapperThermalChangedCallback + mTemperatureCallback = + new ThermalManagerService.ThermalHalWrapper.WrapperThermalChangedCallback() { + @Override + public void onTemperatureChanged(Temperature temperature) { + mTemperatureFuture.complete(temperature); + } + + @Override + public void onThresholdChanged(TemperatureThreshold threshold) { + mThresholdFuture.complete(threshold); + } + }; private ThermalManagerService.ThermalHalAidlWrapper mAidlWrapper; @Captor ArgumentCaptor<IThermalChangedCallback> mAidlCallbackCaptor; @@ -60,27 +87,63 @@ public class ThermalManagerServiceMockingTest { Mockito.when(mAidlHalMock.asBinder()).thenReturn(mAidlBinder); mAidlBinder.attachInterface(mAidlHalMock, IThermal.class.getName()); mTemperatureFuture = new CompletableFuture<>(); - mTemperatureCallback = temperature -> mTemperatureFuture.complete(temperature); + mThresholdFuture = new CompletableFuture<>(); mAidlWrapper = new ThermalManagerService.ThermalHalAidlWrapper(mTemperatureCallback); mAidlWrapper.initProxyAndRegisterCallback(mAidlBinder); } @Test + @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK}) public void setCallback_aidl() throws Exception { Mockito.verify(mAidlHalMock, Mockito.times(1)).registerThermalChangedCallback( mAidlCallbackCaptor.capture()); - android.hardware.thermal.Temperature halT = + android.hardware.thermal.Temperature halTemperature = new android.hardware.thermal.Temperature(); - halT.type = TemperatureType.SOC; - halT.name = "test"; - halT.throttlingStatus = ThrottlingSeverity.SHUTDOWN; - halT.value = 99.0f; - mAidlCallbackCaptor.getValue().notifyThrottling(halT); + halTemperature.type = TemperatureType.SOC; + halTemperature.name = "test"; + halTemperature.throttlingStatus = ThrottlingSeverity.SHUTDOWN; + halTemperature.value = 99.0f; + + android.hardware.thermal.TemperatureThreshold halThreshold = + new android.hardware.thermal.TemperatureThreshold(); + halThreshold.type = TemperatureType.SKIN; + halThreshold.name = "test"; + halThreshold.hotThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1]; + Arrays.fill(halThreshold.hotThrottlingThresholds, Float.NaN); + halThreshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE] = 44.0f; + + mAidlCallbackCaptor.getValue().notifyThrottling(halTemperature); + mAidlCallbackCaptor.getValue().notifyThresholdChanged(halThreshold); + Temperature temperature = mTemperatureFuture.get(100, TimeUnit.MILLISECONDS); - assertEquals(halT.name, temperature.getName()); - assertEquals(halT.type, temperature.getType()); - assertEquals(halT.value, temperature.getValue(), 0.1f); - assertEquals(halT.throttlingStatus, temperature.getStatus()); + assertEquals(halTemperature.name, temperature.getName()); + assertEquals(halTemperature.type, temperature.getType()); + assertEquals(halTemperature.value, temperature.getValue(), 0.1f); + assertEquals(halTemperature.throttlingStatus, temperature.getStatus()); + + TemperatureThreshold threshold = mThresholdFuture.get(100, TimeUnit.MILLISECONDS); + assertEquals(halThreshold.name, threshold.name); + assertEquals(halThreshold.type, threshold.type); + assertArrayEquals(halThreshold.hotThrottlingThresholds, threshold.hotThrottlingThresholds, + 0.01f); + } + + @Test + @DisableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK}) + public void setCallback_aidl_allow_thermal_thresholds_callback_false() throws Exception { + Mockito.verify(mAidlHalMock, Mockito.times(1)).registerThermalChangedCallback( + mAidlCallbackCaptor.capture()); + android.hardware.thermal.TemperatureThreshold halThreshold = + new android.hardware.thermal.TemperatureThreshold(); + halThreshold.type = TemperatureType.SOC; + halThreshold.name = "test"; + halThreshold.hotThrottlingThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1]; + Arrays.fill(halThreshold.hotThrottlingThresholds, Float.NaN); + halThreshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE] = 44.0f; + + mAidlCallbackCaptor.getValue().notifyThresholdChanged(halThreshold); + Thread.sleep(1000); + assertFalse(mThresholdFuture.isDone()); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java index 87a26d0c68e7..8239e0955032 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java @@ -265,7 +265,7 @@ public class BatteryUsageStatsProviderTest { MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext, mock(PowerStatsStore.class), mStatsRule.getPowerProfile(), - mStatsRule.getCpuScalingPolicies(), () -> 3500, new PowerStatsUidResolver()); + mStatsRule.getCpuScalingPolicies(), () -> 3500); powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_AUDIO, true); powerAttributor.setPowerComponentSupported(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, @@ -613,7 +613,7 @@ public class BatteryUsageStatsProviderTest { MultiStatePowerAttributor powerAttributor = new MultiStatePowerAttributor(mContext, powerStatsStore, mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), - () -> 3500, new PowerStatsUidResolver()); + () -> 3500); for (int powerComponentId = 0; powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT; powerComponentId++) { powerAttributor.setPowerComponentSupported(powerComponentId, true); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java index b412ad6edbca..2ff7dde3255f 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java @@ -41,7 +41,6 @@ import androidx.annotation.NonNull; import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerStats; import com.android.server.power.stats.MockClock; -import com.android.server.power.stats.PowerStatsUidResolver; import com.android.server.power.stats.format.BinaryStatePowerStatsLayout; import org.junit.Rule; @@ -63,12 +62,10 @@ public class BinaryStatePowerStatsProcessorTest { private final MockClock mClock = new MockClock(); private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock); - private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver(); private static class TestBinaryStatePowerStatsProcessor extends BinaryStatePowerStatsProcessor { - TestBinaryStatePowerStatsProcessor(int powerComponentId, - double averagePowerMilliAmp, PowerStatsUidResolver uidResolver) { - super(powerComponentId, uidResolver, averagePowerMilliAmp); + TestBinaryStatePowerStatsProcessor(int powerComponentId, double averagePowerMilliAmp) { + super(powerComponentId, averagePowerMilliAmp); } @Override @@ -83,7 +80,7 @@ public class BinaryStatePowerStatsProcessorTest { PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( () -> new TestBinaryStatePowerStatsProcessor( - POWER_COMPONENT, /* averagePowerMilliAmp */ 100, mUidResolver)); + POWER_COMPONENT, /* averagePowerMilliAmp */ 100)); stats.noteStateChange(buildHistoryItem(0, true, APP_UID1)); @@ -161,7 +158,7 @@ public class BinaryStatePowerStatsProcessorTest { PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( () -> new TestBinaryStatePowerStatsProcessor( - POWER_COMPONENT, /* averagePowerMilliAmp */ 100, mUidResolver)); + POWER_COMPONENT, /* averagePowerMilliAmp */ 100)); stats.start(0); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java index 0afcbf15415c..23642de466a1 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java @@ -125,7 +125,7 @@ public class CameraPowerStatsTest { .thenReturn(new int[]{ENERGY_CONSUMER_ID}); PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( - () -> new CameraPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver)); + () -> new CameraPowerStatsProcessor(mStatsRule.getPowerProfile())); CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector); collector.addConsumer( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java index e6207d48d8a0..c63267ec6ae7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java @@ -130,7 +130,7 @@ public class GnssPowerStatsTest { .thenReturn(new int[0]); PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( - () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver)); + () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile())); GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); collector.addConsumer( @@ -220,7 +220,7 @@ public class GnssPowerStatsTest { .thenReturn(new int[0]); PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( - () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver)); + () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile())); stats.noteStateChange(buildHistoryItemInitialStateGpsOn(0)); @@ -299,7 +299,7 @@ public class GnssPowerStatsTest { .thenReturn(new int[]{ENERGY_CONSUMER_ID}); PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats( - () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver)); + () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile())); GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector); collector.addConsumer( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java index 8475d5696252..4643dddb1a33 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java @@ -45,7 +45,6 @@ import com.android.internal.os.PowerStats; import com.android.server.power.stats.BatteryUsageStatsRule; import com.android.server.power.stats.MockClock; import com.android.server.power.stats.PowerStatsStore; -import com.android.server.power.stats.PowerStatsUidResolver; import com.android.server.power.stats.format.CpuPowerStatsLayout; import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout; @@ -132,8 +131,8 @@ public class PowerStatsExporterTest { null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras); mPowerAttributor = new MultiStatePowerAttributor(mock(Context.class), mPowerStatsStore, - mock(PowerProfile.class), mock(CpuScalingPolicies.class), () -> 3500, - mock(PowerStatsUidResolver.class)); + mock(PowerProfile.class), mock(CpuScalingPolicies.class), () -> 3500 + ); } @Test diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java index 2b55303bd89f..feb00e760ce6 100644 --- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java +++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java @@ -19,23 +19,35 @@ package com.android.server.security.forensic; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; import android.content.Context; import android.os.Looper; import android.os.RemoteException; import android.os.test.TestLooper; +import android.security.forensic.ForensicEvent; import android.security.forensic.IForensicServiceCommandCallback; import android.security.forensic.IForensicServiceStateCallback; +import android.util.ArrayMap; + +import com.android.server.ServiceThread; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + public class ForensicServiceTest { private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN; private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE; @@ -55,10 +67,12 @@ public class ForensicServiceTest { @Mock private Context mContext; private BackupTransportConnection mBackupTransportConnection; - + private DataAggregator mDataAggregator; private ForensicService mForensicService; private TestLooper mTestLooper; private Looper mLooper; + private TestLooper mTestLooperOfDataAggregator; + private Looper mLooperOfDataAggregator; @SuppressLint("VisibleForTests") @Before @@ -67,6 +81,8 @@ public class ForensicServiceTest { mTestLooper = new TestLooper(); mLooper = mTestLooper.getLooper(); + mTestLooperOfDataAggregator = new TestLooper(); + mLooperOfDataAggregator = mTestLooperOfDataAggregator.getLooper(); mForensicService = new ForensicService(new MockInjector(mContext)); mForensicService.onStart(); } @@ -121,6 +137,8 @@ public class ForensicServiceTest { assertEquals(STATE_INVISIBLE, scb1.mState); assertEquals(STATE_INVISIBLE, scb2.mState); + doReturn(true).when(mDataAggregator).initialize(); + CommandCallback ccb = new CommandCallback(); mForensicService.getBinderService().makeVisible(ccb); mTestLooper.dispatchAll(); @@ -130,6 +148,29 @@ public class ForensicServiceTest { } @Test + public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable() + throws RemoteException { + mForensicService.setState(STATE_INVISIBLE); + StateCallback scb1 = new StateCallback(); + StateCallback scb2 = new StateCallback(); + mForensicService.getBinderService().monitorState(scb1); + mForensicService.getBinderService().monitorState(scb2); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + + doReturn(false).when(mDataAggregator).initialize(); + + CommandCallback ccb = new CommandCallback(); + mForensicService.getBinderService().makeVisible(ccb); + mTestLooper.dispatchAll(); + assertEquals(STATE_INVISIBLE, scb1.mState); + assertEquals(STATE_INVISIBLE, scb2.mState); + assertNotNull(ccb.mErrorCode); + assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue()); + } + + @Test public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException { mForensicService.setState(STATE_VISIBLE); StateCallback scb1 = new StateCallback(); @@ -262,6 +303,8 @@ public class ForensicServiceTest { CommandCallback ccb = new CommandCallback(); mForensicService.getBinderService().enable(ccb); mTestLooper.dispatchAll(); + + verify(mDataAggregator, times(1)).enable(); assertEquals(STATE_ENABLED, scb1.mState); assertEquals(STATE_ENABLED, scb2.mState); assertNull(ccb.mErrorCode); @@ -361,14 +404,67 @@ public class ForensicServiceTest { doNothing().when(mBackupTransportConnection).release(); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + CommandCallback ccb = new CommandCallback(); mForensicService.getBinderService().disable(ccb); mTestLooper.dispatchAll(); + mTestLooperOfDataAggregator.dispatchAll(); + // TODO: We can verify the data sources once we implement them. + verify(mockThread, times(1)).quitSafely(); assertEquals(STATE_VISIBLE, scb1.mState); assertEquals(STATE_VISIBLE, scb2.mState); assertNull(ccb.mErrorCode); } + @Test + public void testDataAggregator_AddBatchData() { + mForensicService.setState(STATE_ENABLED); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + + String eventOneType = "event_one_type"; + String eventOneMapKey = "event_one_map_key"; + String eventOneMapVal = "event_one_map_val"; + Map<String, String> eventOneMap = new ArrayMap<String, String>(); + eventOneMap.put(eventOneMapKey, eventOneMapVal); + ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap); + + String eventTwoType = "event_two_type"; + String eventTwoMapKey = "event_two_map_key"; + String eventTwoMapVal = "event_two_map_val"; + Map<String, String> eventTwoMap = new ArrayMap<String, String>(); + eventTwoMap.put(eventTwoMapKey, eventTwoMapVal); + ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap); + + List<ForensicEvent> events = new ArrayList<>(); + events.add(eventOne); + events.add(eventTwo); + + doReturn(true).when(mBackupTransportConnection).addData(any()); + + mDataAggregator.addBatchData(events); + mTestLooperOfDataAggregator.dispatchAll(); + mTestLooper.dispatchAll(); + + ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class); + verify(mBackupTransportConnection).addData(captor.capture()); + List<ForensicEvent> receivedEvents = captor.getValue(); + assertEquals(receivedEvents.size(), 2); + + assertEquals(receivedEvents.getFirst().getType(), eventOneType); + assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1); + assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey), + eventOneMapVal); + + assertEquals(receivedEvents.getLast().getType(), eventTwoType); + assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1); + assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey), + eventTwoMapVal); + + } + private class MockInjector implements ForensicService.Injector { private final Context mContext; @@ -393,6 +489,11 @@ public class ForensicServiceTest { return mBackupTransportConnection; } + @Override + public DataAggregator getDataAggregator(ForensicService forensicService) { + mDataAggregator = spy(new DataAggregator(forensicService)); + return mDataAggregator; + } } private static class StateCallback extends IForensicServiceStateCallback.Stub { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 5985abc344a2..7e0c12a5a545 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -18,7 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; -import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER; +import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; @@ -1484,7 +1484,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void persistScale_setValue_notifyInput() { register(TEST_DISPLAY); @@ -1504,7 +1504,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScale_setNonTransientScale_notifyInput() { register(TEST_DISPLAY); @@ -1516,7 +1516,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScaleAndCenter_setTransientScale_notNotifyInput() { register(TEST_DISPLAY); @@ -1531,7 +1531,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setScaleAndCenter_setNonTransientScale_notifyInput() { register(TEST_DISPLAY); @@ -1543,7 +1543,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void setCenter_notNotifyInput() { register(TEST_DISPLAY); @@ -1561,7 +1561,7 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER) + @RequiresFlagsEnabled(FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX) public void offsetMagnifiedRegion_notNotifyInput() { register(TEST_DISPLAY); diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java index ea70287466ff..4d6f5e5df656 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java @@ -56,6 +56,7 @@ public final class BatteryStatsServiceTest { Handler handler = new Handler(mBgThread.getLooper()); mBatteryStatsService = new BatteryStatsService(context, systemDir); mBatteryStatsService.setRailsStatsCollectionEnabled(false); + mBatteryStatsService.systemServicesReady(); } @After diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java index b7100ea00a40..c9e9f00985f1 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java @@ -15,6 +15,8 @@ */ package com.android.server.audio; +import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC; + import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -27,16 +29,20 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; +import android.media.AudioDeviceAttributes; import android.media.AudioSystem; import android.os.Looper; import android.os.PermissionEnforcer; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; +import com.android.media.flags.Flags; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -53,6 +59,7 @@ public class AudioServiceTest { private static final String TAG = "AudioServiceTest"; private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100; + private static final int DEFAULT_INPUT_GAIN_INDEX = 50; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -202,4 +209,29 @@ public class AudioServiceTest { reset(mSpySystemServer); } } + + /** Test input gain index setter and getter */ + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void testInputGainIndex() throws Exception { + Log.i(TAG, "running testInputGainIndex"); + Assert.assertNotNull(mAudioService); + Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization + + AudioDeviceAttributes ada = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_INPUT, TYPE_BUILTIN_MIC, /* address= */ ""); + + Assert.assertEquals( + "default input gain index reporting wrong value", + DEFAULT_INPUT_GAIN_INDEX, + mAudioService.getInputGainIndex(ada)); + + int inputGainIndex = 20; + mAudioService.setInputGainIndex(ada, inputGainIndex); + Assert.assertEquals( + "input gain index reporting wrong value", + inputGainIndex, + mAudioService.getInputGainIndex(ada)); + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index a2e6d4c7bfed..93aa10b9112f 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -135,7 +135,6 @@ public class AppIntegrityManagerServiceImplTest { @Mock PlatformCompat mPlatformCompat; @Mock Context mMockContext; @Mock Resources mMockResources; - @Mock IntegrityFileManager mIntegrityFileManager; @Mock Handler mHandler; private final Context mRealContext = InstrumentationRegistry.getTargetContext(); @@ -169,7 +168,6 @@ public class AppIntegrityManagerServiceImplTest { new AppIntegrityManagerServiceImpl( mMockContext, mPackageManagerInternal, - mIntegrityFileManager, mHandler); mSpyPackageManager = spy(mRealContext.getPackageManager()); @@ -177,7 +175,6 @@ public class AppIntegrityManagerServiceImplTest { when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager); when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {}); - when(mIntegrityFileManager.initialized()).thenReturn(true); // These are needed to override the Settings.Global.get result. when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver()); setIntegrityCheckIncludesRuleProvider(true); @@ -224,71 +221,6 @@ public class AppIntegrityManagerServiceImplTest { 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); } - @Test - public void handleBroadcast_notInitialized() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - when(mIntegrityFileManager.initialized()).thenReturn(false); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - Intent intent = makeVerificationIntent(); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - // The evaluation will still run since we still evaluate manifest based rules. - verify(mPackageManagerInternal) - .setIntegrityVerificationResult( - 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); - } - - @Test - public void verifierAsInstaller_skipIntegrityVerification() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - setIntegrityCheckIncludesRuleProvider(false); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext, atLeastOnce()) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - verify(mPackageManagerInternal) - .setIntegrityVerificationResult( - 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); - } - - @Test - public void getCurrentRules() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY); - when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule)); - - assertThat(mService.getCurrentRules().getList()).containsExactly(rule); - } - - @Test - public void getWhitelistedRuleProviders_returnsEmptyForNonSystemApps() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(false); - - assertThat(mService.getWhitelistedRuleProviders()).isEmpty(); - } - - @Test - public void getWhitelistedRuleProviders() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - - assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE); - } - private void allowlistUsAsRuleProvider() { Resources mockResources = mock(Resources.class); when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages)) diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java deleted file mode 100644 index 096c80b13b89..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity; - -import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.AtomicFormula.LongAtomicFormula; -import android.content.integrity.AtomicFormula.StringAtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.Rule; -import android.util.Slog; - -import com.android.server.integrity.parser.RuleBinaryParser; -import com.android.server.integrity.serializer.RuleBinarySerializer; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** Unit test for {@link IntegrityFileManager} */ -@RunWith(JUnit4.class) -public class IntegrityFileManagerTest { - private static final String TAG = "IntegrityFileManagerTest"; - - private static final String VERSION = "version"; - private static final String RULE_PROVIDER = "rule_provider"; - - private File mTmpDir; - - // under test - private IntegrityFileManager mIntegrityFileManager; - - @Before - public void setUp() throws Exception { - mTmpDir = Files.createTempDirectory("IntegrityFileManagerTest").toFile(); - Slog.i(TAG, "Using temp directory " + mTmpDir); - - // Use Xml Parser/Serializer to help with debugging since we can just print the file. - mIntegrityFileManager = - new IntegrityFileManager( - new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir); - Files.walk(mTmpDir.toPath()) - .forEach( - path -> { - Slog.i(TAG, "before " + path); - }); - } - - @After - public void tearDown() throws Exception { - Files.walk(mTmpDir.toPath()) - .forEach( - path -> { - Slog.i(TAG, "after " + path); - }); - // Sorting paths in reverse order guarantees that we delete inside files before deleting - // directory. - Files.walk(mTmpDir.toPath()) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - - @Test - public void testGetMetadata() throws Exception { - assertThat(mIntegrityFileManager.readMetadata()).isNull(); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - - assertThat(mIntegrityFileManager.readMetadata()).isNotNull(); - assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION); - assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER); - } - - @Test - public void testIsInitialized() throws Exception { - assertThat(mIntegrityFileManager.initialized()).isFalse(); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertThat(mIntegrityFileManager.initialized()).isTrue(); - } - - @Test - public void testGetRules() throws Exception { - String packageName = "package"; - String packageCert = "cert"; - int version = 123; - Rule packageNameRule = getPackageNameIndexedRule(packageName); - Rule packageCertRule = getAppCertificateIndexedRule(packageCert); - Rule versionCodeRule = - new Rule( - new LongAtomicFormula( - AtomicFormula.VERSION_CODE, AtomicFormula.EQ, version), - Rule.DENY); - Rule randomRule = - new Rule( - new CompoundFormula( - CompoundFormula.OR, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - "abc", - /* isHashedValue= */ false), - new LongAtomicFormula( - AtomicFormula.VERSION_CODE, - AtomicFormula.EQ, - version))), - Rule.DENY); - - List<Rule> rules = - Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName(packageName) - .setAppCertificates(Collections.singletonList(packageCert)) - .setAppCertificateLineage(Collections.singletonList(packageCert)) - .setVersionCode(version) - .setInstallerName("abc") - .setInstallerCertificates(Collections.singletonList("abc")) - .setIsPreInstalled(true) - .build(); - List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - - assertThat(rulesFetched) - .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule); - } - - @Test - public void testGetRules_indexedForManyRules() throws Exception { - String packageName = "package"; - String installerName = "installer"; - String appCertificate = "cert"; - - // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and - // 500 unindexed rules. - List<Rule> rules = new ArrayList<>(); - int unindexedRuleCount = 70; - - for (int i = 0; i < 2500; i++) { - rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i))); - rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i))); - } - - for (int i = 0; i < unindexedRuleCount; i++) { - rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i))); - } - - // Write the rules and get them indexed. - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); - - // Read the rules for a specific rule. - String installedPackageName = String.format("%s%04d", packageName, 264); - String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName(installedPackageName) - .setAppCertificates(Collections.singletonList(installedAppCertificate)) - .setAppCertificateLineage( - Collections.singletonList(installedAppCertificate)) - .setVersionCode(250) - .setInstallerName("abc") - .setInstallerCertificates(Collections.singletonList("abc")) - .setIsPreInstalled(true) - .build(); - List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - - // Verify that we do not load all the rules and we have the necessary rules to evaluate. - assertThat(rulesFetched.size()) - .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount); - assertThat(rulesFetched) - .containsAtLeast( - getPackageNameIndexedRule(installedPackageName), - getAppCertificateIndexedRule(installedAppCertificate)); - } - - private Rule getPackageNameIndexedRule(String packageName) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false), - Rule.DENY); - } - - private Rule getAppCertificateIndexedRule(String appCertificate) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, /* isHashedValue= */ false), - Rule.DENY); - } - - private Rule getInstallerCertificateRule(String installerCert) { - return new Rule( - new StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */false), - Rule.DENY); - } - - @Test - public void testStagingDirectoryCleared() throws Exception { - // We must push rules two times to ensure that staging directory is empty because we cleared - // it, rather than because original rules directory is empty. - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - - assertStagingDirectoryCleared(); - } - - private void assertStagingDirectoryCleared() { - File stagingDir = new File(mTmpDir, "integrity_staging"); - assertThat(stagingDir.exists()).isTrue(); - assertThat(stagingDir.isDirectory()).isTrue(); - assertThat(stagingDir.listFiles()).isEmpty(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java index 41cb6fd99d9b..e1dcc99d3851 100644 --- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java @@ -128,8 +128,6 @@ public class ContextHubEventLoggerTest { } @Test - @EnableFlags({Flags.FLAG_RELIABLE_MESSAGE, - Flags.FLAG_RELIABLE_MESSAGE_IMPLEMENTATION}) public void testLogReliableMessageToNanoappStatus() { NanoAppMessage message1 = NanoAppMessage.createMessageToNanoApp(1, 0, new byte[] {0x00, 0x11, 0x22, 0x33}); diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 6d79ae467bf0..cfe3d84140df 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -296,11 +296,11 @@ public class ThermalManagerServiceTest { } @Test - public void testNotify() throws RemoteException { + public void testNotifyThrottling() throws RemoteException { int status = Temperature.THROTTLING_SEVERE; // Should only notify event not status Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status); - mFakeHal.mCallback.onValues(newBattery); + mFakeHal.mCallback.onTemperatureChanged(newBattery); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newBattery); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) @@ -312,7 +312,7 @@ public class ThermalManagerServiceTest { resetListenerMock(); // Notify both event and status Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status); - mFakeHal.mCallback.onValues(newSkin); + mFakeHal.mCallback.onTemperatureChanged(newSkin); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newSkin); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) @@ -325,7 +325,7 @@ public class ThermalManagerServiceTest { // Back to None, should only notify event not status status = Temperature.THROTTLING_NONE; newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status); - mFakeHal.mCallback.onValues(newBattery); + mFakeHal.mCallback.onTemperatureChanged(newBattery); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newBattery); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) @@ -337,7 +337,7 @@ public class ThermalManagerServiceTest { resetListenerMock(); // Should also notify status newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status); - mFakeHal.mCallback.onValues(newSkin); + mFakeHal.mCallback.onTemperatureChanged(newSkin); verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).notifyThrottling(newSkin); verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) @@ -362,7 +362,7 @@ public class ThermalManagerServiceTest { public void testGetCurrentStatus() throws RemoteException { int status = Temperature.THROTTLING_SEVERE; Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); - mFakeHal.mCallback.onValues(newSkin); + mFakeHal.mCallback.onTemperatureChanged(newSkin); assertEquals(status, mService.mService.getCurrentThermalStatus()); int battStatus = Temperature.THROTTLING_EMERGENCY; Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", battStatus); @@ -373,11 +373,11 @@ public class ThermalManagerServiceTest { public void testThermalShutdown() throws RemoteException { int status = Temperature.THROTTLING_SHUTDOWN; Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); - mFakeHal.mCallback.onValues(newSkin); + mFakeHal.mCallback.onTemperatureChanged(newSkin); verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false); Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", status); - mFakeHal.mCallback.onValues(newBattery); + mFakeHal.mCallback.onTemperatureChanged(newBattery); verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC) .times(1)).shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false); } @@ -419,15 +419,35 @@ public class ThermalManagerServiceTest { } @Test - public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException { + public void testTemperatureWatcherUpdateSevereThresholds() { TemperatureWatcher watcher = mService.mTemperatureWatcher; - watcher.mSevereThresholds.erase(); - watcher.updateThresholds(); - assertEquals(1, watcher.mSevereThresholds.size()); - assertEquals("skin1", watcher.mSevereThresholds.keyAt(0)); - Float threshold = watcher.mSevereThresholds.get("skin1"); - assertNotNull(threshold); - assertEquals(40.0f, threshold, 0.0f); + synchronized (watcher.mSamples) { + watcher.mSevereThresholds.erase(); + watcher.getAndUpdateThresholds(); + assertEquals(1, watcher.mSevereThresholds.size()); + assertEquals("skin1", watcher.mSevereThresholds.keyAt(0)); + Float threshold = watcher.mSevereThresholds.get("skin1"); + assertNotNull(threshold); + assertEquals(40.0f, threshold, 0.0f); + assertArrayEquals("Got" + Arrays.toString(watcher.mHeadroomThresholds), + new float[]{Float.NaN, 0.6667f, 0.8333f, 1.0f, 1.166f, 1.3333f, + 1.5f}, + watcher.mHeadroomThresholds, 0.01f); + + TemperatureThreshold newThreshold = new TemperatureThreshold(); + newThreshold.name = "skin1"; + newThreshold.hotThrottlingThresholds = new float[] { + Float.NaN, 44.0f, 47.0f, 50.0f, Float.NaN, Float.NaN, Float.NaN + }; + mFakeHal.mCallback.onThresholdChanged(newThreshold); + threshold = watcher.mSevereThresholds.get("skin1"); + assertNotNull(threshold); + assertEquals(50.0f, threshold, 0.0f); + assertArrayEquals("Got" + Arrays.toString(watcher.mHeadroomThresholds), + new float[]{Float.NaN, 0.8f, 0.9f, 1.0f, Float.NaN, Float.NaN, + Float.NaN}, + watcher.mHeadroomThresholds, 0.01f); + } } @Test @@ -436,25 +456,19 @@ public class ThermalManagerServiceTest { synchronized (watcher.mSamples) { Arrays.fill(watcher.mHeadroomThresholds, Float.NaN); } - watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 40, 49); - watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 49); - watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 49, 49); - watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 49); - watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 70, 49); - watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 79, 49); + TemperatureThreshold threshold = new TemperatureThreshold(); + threshold.hotThrottlingThresholds = new float[]{Float.NaN, 40, 46, 49, 64, 70, 79}; synchronized (watcher.mSamples) { + watcher.updateTemperatureThresholdLocked(threshold, false /*override*/); assertArrayEquals(new float[]{Float.NaN, 0.7f, 0.9f, 1.0f, 1.5f, 1.7f, 2.0f}, watcher.mHeadroomThresholds, 0.01f); } // when another sensor reports different threshold, we expect to see smaller one to be used - watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 37, 52); - watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 52); - watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 52, 52); - watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 52); - watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 100, 52); - watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 200, 52); + threshold = new TemperatureThreshold(); + threshold.hotThrottlingThresholds = new float[]{Float.NaN, 37, 46, 52, 64, 100, 200}; synchronized (watcher.mSamples) { + watcher.updateTemperatureThresholdLocked(threshold, false /*override*/); assertArrayEquals(new float[]{Float.NaN, 0.5f, 0.8f, 1.0f, 1.4f, 1.7f, 2.0f}, watcher.mHeadroomThresholds, 0.01f); } @@ -486,7 +500,7 @@ public class ThermalManagerServiceTest { TemperatureWatcher watcher = mService.mTemperatureWatcher; ArrayList<TemperatureThreshold> thresholds = new ArrayList<>(); mFakeHal.mTemperatureThresholdList = thresholds; - watcher.updateThresholds(); + watcher.getAndUpdateThresholds(); synchronized (watcher.mSamples) { assertArrayEquals( new float[]{Float.NaN, Float.NaN, Float.NaN, Float.NaN, Float.NaN, Float.NaN, @@ -501,7 +515,7 @@ public class ThermalManagerServiceTest { Arrays.fill(nanThresholds.hotThrottlingThresholds, Float.NaN); Arrays.fill(nanThresholds.coldThrottlingThresholds, Float.NaN); thresholds.add(nanThresholds); - watcher.updateThresholds(); + watcher.getAndUpdateThresholds(); synchronized (watcher.mSamples) { assertArrayEquals( new float[]{Float.NaN, Float.NaN, Float.NaN, Float.NaN, Float.NaN, Float.NaN, diff --git a/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java b/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java index 6b32be0b2dfd..1af366b32da9 100644 --- a/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java +++ b/services/tests/servicestests/src/com/android/server/power/WakefulnessSessionObserverTest.java @@ -28,6 +28,7 @@ import static android.view.Display.DEFAULT_DISPLAY_GROUP; import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_UNKNOWN; import static com.android.server.power.ScreenTimeoutOverridePolicy.RELEASE_REASON_USER_ACTIVITY_TOUCH; import static com.android.server.power.WakefulnessSessionObserver.OFF_REASON_POWER_BUTTON; +import static com.android.server.power.WakefulnessSessionObserver.OFF_REASON_TIMEOUT; import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_CANCEL_POWER_BUTTON; import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_CANCEL_USER_INTERACTION; import static com.android.server.power.WakefulnessSessionObserver.OVERRIDE_OUTCOME_TIMEOUT_SUCCESS; @@ -40,6 +41,7 @@ import static com.android.server.power.WakefulnessSessionObserver.POLICY_REASON_ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -90,6 +92,7 @@ public class WakefulnessSessionObserverTest { mWakefulnessSessionFrameworkStatsLogger; @Mock private DisplayManagerInternal mDisplayManagerInternal; + private MockContentResolver mContentResolver = new MockContentResolver(); private TestHandler mHandler; @Before @@ -106,10 +109,9 @@ public class WakefulnessSessionObserverTest { R.integer.config_screenTimeoutOverride); when(mContext.getResources()).thenReturn(res); FakeSettingsProvider.clearSettingsProvider(); - MockContentResolver mockContentResolver = new MockContentResolver(); - mockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - when(mContext.getContentResolver()).thenReturn(mockContentResolver); - Settings.System.putIntForUser(mockContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT_MS, UserHandle.USER_CURRENT); final DisplayInfo info = new DisplayInfo(); @@ -511,6 +513,115 @@ public class WakefulnessSessionObserverTest { DEFAULT_SCREEN_OFF_TIMEOUT_MS); // default Timeout Ms } + @Test + public void testScreenOffTimeout_normal_logSessionEventTriggered() { + int powerGroupId = 1; + int userActivity = PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY; + triggerLogSessionEvent(powerGroupId, userActivity); + verify(mWakefulnessSessionFrameworkStatsLogger) + .logSessionEvent( + powerGroupId, // powerGroupId + OFF_REASON_TIMEOUT, // interactiveStateOffReason + 0, // interactiveStateOnDurationMs + userActivity, // userActivity + 0, // lastUserActivityEventDurationMs + DEFAULT_SCREEN_OFF_TIMEOUT_MS - OVERRIDE_SCREEN_OFF_TIMEOUT_MS + ); // reducedInteractiveStateOnDurationMs; + } + + @Test + public void testScreenOffTimeout_zero_noLogSessionEventTriggered() { + // simulate adding an invalid screen_off_timeout value + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + 0, // invalid timeout value + UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + + try { + triggerLogSessionEvent(); + verify(mWakefulnessSessionFrameworkStatsLogger, never()) + .logSessionEvent(anyInt(), anyInt(), anyLong(), anyInt(), anyLong(), anyInt()); + } finally { + // rollback the original data + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + DEFAULT_SCREEN_OFF_TIMEOUT_MS, UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + } + } + + @Test + public void testScreenOffTimeout_negative_noLogSessionEventTriggered() { + // simulate adding an invalid screen_off_timeout value + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + -1, // invalid timeout value + UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + + try { + triggerLogSessionEvent(); + verify(mWakefulnessSessionFrameworkStatsLogger, never()) + .logSessionEvent(anyInt(), anyInt(), anyLong(), anyInt(), anyLong(), anyInt()); + } finally { + // rollback the original data + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + DEFAULT_SCREEN_OFF_TIMEOUT_MS, UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + } + } + + @Test + public void testScreenOffTimeout_max_logSessionEventTriggered() { + // simulate adding the max screen_off_timeout value + int defaultTimeoutMs = Integer.MAX_VALUE; + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + defaultTimeoutMs, + UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + + try { + int powerGroupId = 1; + int userActivity = PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY; + triggerLogSessionEvent(powerGroupId, userActivity); + verify(mWakefulnessSessionFrameworkStatsLogger) + .logSessionEvent( + powerGroupId, // powerGroupId + OFF_REASON_TIMEOUT, // interactiveStateOffReason + 0, // interactiveStateOnDurationMs + userActivity, // userActivity + 0, // lastUserActivityEventDurationMs + defaultTimeoutMs - OVERRIDE_SCREEN_OFF_TIMEOUT_MS + ); // reducedInteractiveStateOnDurationMs; + } finally { + // rollback the original data + Settings.System.putIntForUser(mContentResolver, Settings.System.SCREEN_OFF_TIMEOUT, + DEFAULT_SCREEN_OFF_TIMEOUT_MS, UserHandle.USER_CURRENT); + mWakefulnessSessionObserver.updateSettingScreenOffTimeout(mContext); + } + } + + private void triggerLogSessionEvent() { + triggerLogSessionEvent(1, PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY); + } + + private void triggerLogSessionEvent(int powerGroupId, int userActivity) { + mWakefulnessSessionObserver.onWakefulnessChangeStarted( + powerGroupId, + PowerManagerInternal.WAKEFULNESS_AWAKE, + WAKE_REASON_POWER_BUTTON, + mTestClock.now()); + mWakefulnessSessionObserver.onWakeLockAcquired( + PowerManager.SCREEN_TIMEOUT_OVERRIDE_WAKE_LOCK); + + long userActivityTime = mTestClock.now(); + mWakefulnessSessionObserver.notifyUserActivity( + userActivityTime, powerGroupId, userActivity); + mWakefulnessSessionObserver.onWakefulnessChangeStarted( + powerGroupId, + PowerManagerInternal.WAKEFULNESS_DOZING, + GO_TO_SLEEP_REASON_TIMEOUT, + mTestClock.now()); + } + private void advanceTime(long timeMs) { mTestClock.fastForward(timeMs); } diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS b/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS new file mode 100644 index 000000000000..9bf5e58c01a9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/OWNERS @@ -0,0 +1 @@ +file:platform/frameworks/base:main:/core/java/android/security/advancedprotection/OWNERS diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java index 05210aca19dd..fa1372d9f4ef 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java @@ -124,9 +124,12 @@ public class EventConditionProviderTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) - public void onBootComplete_waitsForUserSwitched() { + public void onBootComplete_loadsTrackersForSystemUser() { mService.onBootComplete(); - assertThat(mService.getTrackers().size()).isEqualTo(0); + + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(UserHandle.USER_SYSTEM); } @Test @@ -158,4 +161,21 @@ public class EventConditionProviderTest extends UiServiceTestCase { assertThat(mService.getTrackers().keyAt(1)).isEqualTo(43); assertThat(mService.getTrackers().valueAt(1).getUserId()).isEqualTo(43); } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onUserSwitched_sameUser_doesNothing() { + UserHandle someUser = UserHandle.of(42); + when(mUserManager.getProfiles(eq(42))).thenReturn(List.of(new UserInfo(42, "user 42", 0))); + + mService.onUserSwitched(someUser); + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + CalendarTracker originalTracker = mService.getTrackers().valueAt(0); + + mService.onUserSwitched(someUser); + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + assertThat(mService.getTrackers().valueAt(0)).isSameInstanceAs(originalTracker); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index e845d80b412a..dec7f09f2da4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -45,6 +45,9 @@ import static android.app.Notification.GROUP_ALERT_CHILDREN; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.NotificationChannel.NEWS_ID; import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; +import static android.app.NotificationChannel.PROMOTIONS_ID; +import static android.app.NotificationChannel.RECS_ID; +import static android.app.NotificationChannel.SOCIAL_MEDIA_ID; import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; @@ -98,7 +101,10 @@ import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_TEXT_REPLIES; import static android.service.notification.Adjustment.KEY_TYPE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; +import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; import static android.service.notification.Adjustment.TYPE_NEWS; +import static android.service.notification.Adjustment.TYPE_PROMOTION; +import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; import static android.service.notification.Condition.SOURCE_CONTEXT; import static android.service.notification.Condition.SOURCE_USER_ACTION; import static android.service.notification.Condition.STATE_TRUE; @@ -16767,6 +16773,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { r.applyAdjustments(); assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); + + signals.putInt(KEY_TYPE, TYPE_PROMOTION); + mBinderService.applyAdjustmentFromAssistant(null, adjustment); + waitForIdle(); + r.applyAdjustments(); + assertThat(r.getChannel().getId()).isEqualTo(PROMOTIONS_ID); + + signals.putInt(KEY_TYPE, TYPE_SOCIAL_MEDIA); + mBinderService.applyAdjustmentFromAssistant(null, adjustment); + waitForIdle(); + r.applyAdjustments(); + assertThat(r.getChannel().getId()).isEqualTo(SOCIAL_MEDIA_ID); + + signals.putInt(KEY_TYPE, TYPE_CONTENT_RECOMMENDATION); + mBinderService.applyAdjustmentFromAssistant(null, adjustment); + waitForIdle(); + r.applyAdjustments(); + assertThat(r.getChannel().getId()).isEqualTo(RECS_ID); } @Test @@ -17066,7 +17090,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testAppCannotUseReservedBundleChannels() throws Exception { - mBinderService.getBubblePreferenceForPackage(mPkg, mUid); + mService.mPreferencesHelper.createReservedChannel(mPkg, mUid, TYPE_NEWS); NotificationChannel news = mBinderService.getNotificationChannel( mPkg, mContext.getUserId(), mPkg, NEWS_ID); assertThat(news).isNotNull(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index b92bdb5f3e6e..1a1da0f6d408 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -54,6 +54,11 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_SYSTEM; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; +import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION; +import static android.service.notification.Adjustment.TYPE_NEWS; +import static android.service.notification.Adjustment.TYPE_OTHER; +import static android.service.notification.Adjustment.TYPE_PROMOTION; +import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA; import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION; import static android.service.notification.Flags.notificationClassification; @@ -640,6 +645,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationChannel updateNews = null; if (notificationClassification()) { + mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS); // change one of the reserved bundle channels to ensure changes are persisted across // boot updateNews = mHelper.getNotificationChannel( @@ -1210,22 +1216,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" " + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble" + "=\"false\" uid=\"10002\">\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id\" name=\"name\" importance=\"2\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "</package>\n" + "<package name=\"com.example.n_mr1\" show_badge=\"true\" " + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" " @@ -1233,10 +1226,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "=\"false\" uid=\"10001\">\n" + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n" + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" " @@ -1246,15 +1235,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"miscellaneous\" name=\"Uncategorized\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" @@ -1321,22 +1301,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" " + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble" + "=\"false\">\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id\" name=\"name\" importance=\"2\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "</package>\n" // Importance default because on in permission helper + "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" " @@ -1345,10 +1312,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "=\"false\">\n" + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n" + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" " @@ -1358,15 +1321,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"miscellaneous\" name=\"Uncategorized\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" @@ -1433,22 +1387,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" " + "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble" + "=\"false\">\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id\" name=\"name\" importance=\"2\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "</package>\n" // Importance 0 because missing from permission helper + "<package name=\"com.example.n_mr1\" importance=\"0\" show_badge=\"true\" " @@ -1457,10 +1398,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "=\"false\">\n" + "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n" + "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.social\" " - + "name=\"Social\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" + "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" " @@ -1470,15 +1407,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" " + "orig_imp=\"4\" />\n" - + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" " - + "importance=\"2\" sound=\"content://settings/system/notification_sound\" " - + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" - + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" " - + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " - + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "") + "<channel id=\"miscellaneous\" name=\"Uncategorized\" " + "sound=\"content://settings/system/notification_sound\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" @@ -2183,10 +2111,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testUpdate_preUpgrade_updatesAppFields() throws Exception { assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); - assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1)); + assertEquals(Notification.PRIORITY_DEFAULT, + mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1)); assertEquals(VISIBILITY_NO_OVERRIDE, mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1)); @@ -2549,7 +2477,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { List<NotificationChannel> channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList(); // Default channel + non-deleted channel + system defaults - assertEquals(notificationClassification() ? 6 : 2, channels.size()); + assertEquals(2, channels.size()); for (NotificationChannel nc : channels) { if (channel2.getId().equals(nc.getId())) { compareChannels(channel2, nc); @@ -2559,7 +2487,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // Returns deleted channels too channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList(); // Includes system channel(s) - assertEquals(notificationClassification() ? 7 : 3, channels.size()); + assertEquals(3, channels.size()); for (NotificationChannel nc : channels) { if (channel2.getId().equals(nc.getId())) { compareChannels(channelMap.get(nc.getId()), nc); @@ -3036,7 +2964,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testOnlyHasDefaultChannel() throws Exception { assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O)); @@ -3047,6 +2974,18 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testOnlyHasDefaultChannel_bundleExists() throws Exception { + mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS); + assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); + assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O)); + + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false, + UID_N_MR1, false); + assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); + } + + @Test public void testCreateDeletedChannel() throws Exception { long[] vibration = new long[]{100, 67, 145, 156}; NotificationChannel channel = @@ -3315,7 +3254,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // user 0 records remain for (int i = 0; i < user0Uids.length; i++) { - assertEquals(notificationClassification() ? 5 : 1, + assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true) .getList().size()); } @@ -3346,7 +3285,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1})); - assertEquals(notificationClassification() ? 6 : 2, + assertEquals(2, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) .getList().size()); } @@ -3420,7 +3359,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testRecordDefaults() throws Exception { assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); - assertEquals(notificationClassification() ? 5 : 1, + assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) .getList().size()); } @@ -3659,9 +3598,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false, UID_N_MR1, false); } - if (notificationClassification()) { - numChannels += 4; - } expectedChannels.put(pkgName, numChannels); } @@ -4883,10 +4819,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testTooManyChannels() { int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT; - if (notificationClassification()) { - // reserved channels lower limit - numToCreate -= 4; - } for (int i = 0; i < numToCreate; i++) { NotificationChannel channel = new NotificationChannel(String.valueOf(i), String.valueOf(i), NotificationManager.IMPORTANCE_HIGH); @@ -4907,10 +4839,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testTooManyChannels_xml() throws Exception { int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT; - if (notificationClassification()) { - // reserved channels lower limit - numToCreate -= 4; - } String extraChannel = "EXTRA"; String extraChannel1 = "EXTRA1"; @@ -5928,9 +5856,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ArrayList<StatsEvent> events = new ArrayList<>(); mHelper.pullPackageChannelPreferencesStats(events); - assertEquals("expected number of events", - notificationClassification() ? 7 : 3, - events.size()); + assertEquals("expected number of events", 3, events.size()); for (StatsEvent ev : events) { // all of these events should be of PackageNotificationChannelPreferences type, // and therefore we expect the atom to have this field. @@ -5971,17 +5897,11 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_O, UID_O, channelC, true, false, UID_O, false); List<String> channels = new LinkedList<>(Arrays.asList("a", "b", "c")); - if (notificationClassification()) { - channels.add(NEWS_ID); - channels.add(PROMOTIONS_ID); - channels.add(SOCIAL_MEDIA_ID); - channels.add(RECS_ID); - } ArrayList<StatsEvent> events = new ArrayList<>(); mHelper.pullPackageChannelPreferencesStats(events); - assertEquals("total events", notificationClassification() ? 7 : 3, events.size()); + assertEquals("total events", 3, events.size()); for (StatsEvent ev : events) { AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev); assertTrue(atom.hasPackageNotificationChannelPreferences()); @@ -6015,7 +5935,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.pullPackageChannelPreferencesStats(events); // In this case, we want to check the properties of the conversation channel (not parent) - assertEquals("total events", notificationClassification() ? 6 : 2, events.size()); + assertEquals("total events", 2, events.size()); for (StatsEvent ev : events) { AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev); assertTrue(atom.hasPackageNotificationChannelPreferences()); @@ -6047,9 +5967,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ArrayList<StatsEvent> events = new ArrayList<>(); mHelper.pullPackageChannelPreferencesStats(events); - assertEquals("total events", - notificationClassification() ? 6 : 2, - events.size()); + assertEquals("total events", 2, events.size()); for (StatsEvent ev : events) { AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev); assertTrue(atom.hasPackageNotificationChannelPreferences()); @@ -6080,9 +5998,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ArrayList<StatsEvent> events = new ArrayList<>(); mHelper.pullPackageChannelPreferencesStats(events); - assertEquals("total events", - notificationClassification() ? 6 : 2, - events.size()); + assertEquals("total events", 2, events.size()); for (StatsEvent ev : events) { AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev); assertTrue(atom.hasPackageNotificationChannelPreferences()); @@ -6367,8 +6283,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testGetNotificationChannels_omitBundleChannels() { - // do something that triggers settings creation for an app - mHelper.setShowBadge(PKG_O, UID_O, true); + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS); assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty(); } @@ -6376,25 +6291,64 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles() { - // do something that triggers settings creation for an app - mHelper.setShowBadge(PKG_O, UID_O, true); - - // verify 4 reserved channels are created + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS); assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, NEWS_ID, false).getImportance()) .isEqualTo(IMPORTANCE_LOW); + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()) + .isEqualTo(1); + + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_SOCIAL_MEDIA); + assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false). + getImportance()).isEqualTo(IMPORTANCE_LOW); + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()) + .isEqualTo(2); + + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_CONTENT_RECOMMENDATION); + assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance()) + .isEqualTo(IMPORTANCE_LOW); + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()) + .isEqualTo(3); + + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_PROMOTION); assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false) .getImportance()).isEqualTo(IMPORTANCE_LOW); - assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false) + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()) + .isEqualTo(4); + + // only the first 4 types are created; no others + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_OTHER); + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()) + .isEqualTo(4); + } + + @Test + @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testNotificationBundles_off_deletesData() throws Exception { + String xml = "<ranking version=\"1\">\n" + + "<package name=\"" + PKG_P + "\" uid=\"" + UID_P + "\">\n" + + "<channel id=\"android.app.social\" name=\"Social\" importance=\"2\"/>\n" + + "<channel id=\"android.app.news\" name=\"News\" importance=\"2\"/>\n" + + "<channel id=\"android.app.recs\" name=\"Recs\" importance=\"2\"/>\n" + + "<channel id=\"android.app.promotions\" name=\"Promos\" importance=\"2\"/>\n" + + "<channel id=\"keep.me\" name=\"name\" importance=\"2\" " + + "show_badge=\"true\" />\n" + + "</package></ranking>\n"; + + loadByteArrayXml(xml.getBytes(), false, USER_SYSTEM); + + // verify 4 reserved channels are created + assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, NEWS_ID, true)).isNull(); + assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, PROMOTIONS_ID, true)).isNull(); + assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true)).isNull(); + assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, RECS_ID, true)).isNull(); + assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, "keep.me", false) .getImportance()).isEqualTo(IMPORTANCE_LOW); - assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance()) - .isEqualTo(IMPORTANCE_LOW); } @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles_appsCannotUpdate() { - // do something that triggers settings creation for an app - mHelper.setShowBadge(PKG_O, UID_O, true); + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS); NotificationChannel fromApp = new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH); @@ -6407,8 +6361,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles_osCanAllowToBypassDnd() { - // do something that triggers settings creation for an app - mHelper.setShowBadge(PKG_O, UID_O, true); + mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS); NotificationChannel fromApp = new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH); @@ -6418,18 +6371,17 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testUnDeleteBundleChannelsOnLoadIfNotUserChange() throws Exception { - mHelper.setShowBadge(PKG_P, UID_P, true); // the public create/update methods should prevent this, so take advantage of the fact that // the object is in the same process - mHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).setDeleted(true); + mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_SOCIAL_MEDIA).setDeleted(true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, UserHandle.USER_ALL, SOCIAL_MEDIA_ID); loadStreamXml(baos, false, UserHandle.USER_ALL); - assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true). - isDeleted()).isFalse(); + assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, SOCIAL_MEDIA_ID, true) + .isDeleted()).isFalse(); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java index fe4ce465e9be..52c34889a1ec 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java @@ -1,18 +1,38 @@ package com.android.server.notification; +import static android.app.AlarmManager.RTC_WAKEUP; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MINUTES; +import android.app.ActivityManager; +import android.app.AlarmManager; import android.app.Application; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; +import android.os.Bundle; +import android.os.SimpleClock; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.Condition; import android.service.notification.ScheduleCalendar; import android.service.notification.ZenModeConfig; @@ -24,11 +44,20 @@ import androidx.test.filters.SmallTest; import com.android.server.UiServiceTestCase; import com.android.server.pm.PackageManagerService; +import com.google.common.collect.ImmutableList; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Calendar; import java.util.GregorianCalendar; @@ -36,17 +65,22 @@ import java.util.GregorianCalendar; @SmallTest @RunWithLooper public class ScheduleConditionProviderTest extends UiServiceTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - ScheduleConditionProvider mService; + private ScheduleConditionProvider mService; + private TestClock mClock = new TestClock(); + @Mock private AlarmManager mAlarmManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mContext.addMockSystemService(AlarmManager.class, mAlarmManager); Intent startIntent = new Intent("com.android.server.notification.ScheduleConditionProvider"); startIntent.setPackage("android"); - ScheduleConditionProvider service = new ScheduleConditionProvider(); + ScheduleConditionProvider service = new ScheduleConditionProvider(mClock); service.attach( getContext(), null, // ActivityThread not actually used in Service @@ -57,7 +91,7 @@ public class ScheduleConditionProviderTest extends UiServiceTestCase { ); service.onCreate(); service.onBind(startIntent); - mService = spy(service); + mService = service; } @Test @@ -343,6 +377,87 @@ public class ScheduleConditionProviderTest extends UiServiceTestCase { assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage()); } + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onSubscribe_registersReceiverForAllUsers() { + Calendar now = getNow(); + Uri condition = ZenModeConfig.toScheduleConditionId(getScheduleEndsInHour(now)); + + mService.onSubscribe(condition); + + ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class); + verify(mContext).registerReceiverForAllUsers(any(), filterCaptor.capture(), any(), any()); + IntentFilter filter = filterCaptor.getValue(); + assertThat(filter.actionsIterator()).isNotNull(); + assertThat(ImmutableList.copyOf(filter.actionsIterator())) + .contains(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onAlarmClockChanged_storesNextAlarm() { + Instant scheduleStart = Instant.parse("2024-10-22T16:00:00Z"); + Instant scheduleEnd = scheduleStart.plus(1, HOURS); + + Instant now = scheduleStart.plus(15, MINUTES); + mClock.setNowMillis(now.toEpochMilli()); + + Uri condition = ZenModeConfig.toScheduleConditionId( + getOneHourSchedule(scheduleStart.atZone(ZoneId.systemDefault()))); + mService.onSubscribe(condition); + + // Now prepare to send an "alarm set for 16:30" broadcast. + Instant alarm = scheduleStart.plus(30, MINUTES); + ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + verify(mContext).registerReceiverForAllUsers(receiverCaptor.capture(), any(), any(), any()); + BroadcastReceiver receiver = receiverCaptor.getValue(); + receiver.setPendingResult(pendingResultForUserBroadcast(ActivityManager.getCurrentUser())); + when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn( + new AlarmManager.AlarmClockInfo(alarm.toEpochMilli(), null)); + + Intent intent = new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); + receiver.onReceive(mContext, intent); + + // The time for the alarm was stored in the ScheduleCalendar, meaning the rule will end when + // the next evaluation after that point happens. + ScheduleCalendar scheduleCalendar = + mService.getSubscriptions().values().stream().findFirst().get(); + assertThat(scheduleCalendar.shouldExitForAlarm(alarm.toEpochMilli() - 1)).isFalse(); + assertThat(scheduleCalendar.shouldExitForAlarm(alarm.toEpochMilli() + 1)).isTrue(); + + // But the next wakeup is unchanged, at the time of the schedule end (17:00). + verify(mAlarmManager, times(2)).setExact(eq(RTC_WAKEUP), eq(scheduleEnd.toEpochMilli()), + any()); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onAlarmClockChanged_forAnotherUser_isIgnored() { + Instant scheduleStart = Instant.parse("2024-10-22T16:00:00Z"); + Instant now = scheduleStart.plus(15, MINUTES); + mClock.setNowMillis(now.toEpochMilli()); + + Uri condition = ZenModeConfig.toScheduleConditionId( + getOneHourSchedule(scheduleStart.atZone(ZoneId.systemDefault()))); + mService.onSubscribe(condition); + + // Now prepare to send an "alarm set for a different user" broadcast. + ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + verify(mContext).registerReceiverForAllUsers(receiverCaptor.capture(), any(), any(), any()); + BroadcastReceiver receiver = receiverCaptor.getValue(); + + reset(mAlarmManager); + int anotherUser = ActivityManager.getCurrentUser() + 1; + receiver.setPendingResult(pendingResultForUserBroadcast(anotherUser)); + Intent intent = new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); + receiver.onReceive(mContext, intent); + + // The alarm data was not read. + verify(mAlarmManager, never()).getNextAlarmClock(anyInt()); + } + private Calendar getNow() { Calendar now = new GregorianCalendar(); now.set(Calendar.HOUR_OF_DAY, 14); @@ -363,4 +478,38 @@ public class ScheduleConditionProviderTest extends UiServiceTestCase { info.endMinute = now.get(Calendar.MINUTE); return info; } + + private static ZenModeConfig.ScheduleInfo getOneHourSchedule(ZonedDateTime start) { + ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); + // Note: DayOfWeek.MONDAY doesn't match Calendar.MONDAY + info.days = new int[] { (start.getDayOfWeek().getValue() % 7) + 1 }; + info.startHour = start.getHour(); + info.startMinute = start.getMinute(); + info.endHour = start.plusHours(1).getHour(); + info.endMinute = start.plusHours(1).getMinute(); + info.exitAtAlarm = true; + return info; + } + + private static BroadcastReceiver.PendingResult pendingResultForUserBroadcast(int userId) { + return new BroadcastReceiver.PendingResult(0, "", new Bundle(), 0, false, false, null, + userId, 0); + } + + private static class TestClock extends SimpleClock { + private long mNowMillis = 441644400000L; + + private TestClock() { + super(ZoneOffset.UTC); + } + + @Override + public long millis() { + return mNowMillis; + } + + private void setNowMillis(long millis) { + mNowMillis = millis; + } + } } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index 1f167761fc06..132f95bea360 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -19,7 +19,7 @@ filegroup { ], } -genrule { +java_genrule { name: "wmtests.protologsrc", srcs: [ ":protolog-impl", diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java index cdb45423c11a..d83ffd15ff71 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -35,6 +35,7 @@ import android.view.KeyEvent; import androidx.test.filters.MediumTest; +import com.android.hardware.input.Flags; import com.android.internal.annotations.Keep; import junit.framework.Assert; @@ -393,6 +394,17 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { META_ON | CTRL_ON); } + @Test + @EnableFlags(Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) + @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + public void testToggleTalkbackPress() { + testShortcutInternal("Meta + Alt + T -> Toggle talkback", + new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_T}, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK, + KeyEvent.KEYCODE_T, + META_ON | ALT_ON); + } + private void testShortcutInternal(String testName, int[] testKeys, @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey, int expectedModifierState) { @@ -699,4 +711,16 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS)); mPhoneWindowManager.assertCloseAllDialogs(); } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) + public void testKeyGestureToggleTalkback() { + Assert.assertTrue( + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + mPhoneWindowManager.assertTalkBack(true); + + Assert.assertTrue( + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + mPhoneWindowManager.assertTalkBack(false); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java index c186a0355588..28ae271e20fc 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -43,6 +43,8 @@ import static android.view.KeyEvent.KEYCODE_Z; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Intent; +import android.hardware.input.AppLaunchData; +import android.hardware.input.KeyGestureEvent; import android.os.RemoteException; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; @@ -85,7 +87,8 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * Test meta+ shortcuts defined in bookmarks.xml. */ @Test - public void testMetaShortcuts() { + @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + public void testMetaShortcuts_withoutKeyGestureEventHandling() { for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) { final int keyCode = INTENT_SHORTCUTS.keyAt(i); final String category = INTENT_SHORTCUTS.valueAt(i); @@ -115,6 +118,49 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { } + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + public void testMetaShortcuts_withKeyGestureEventHandling() { + for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) { + final String category = INTENT_SHORTCUTS.valueAt(i); + mPhoneWindowManager.sendKeyGestureEvent( + new KeyGestureEvent.Builder() + .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) + .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(category)) + .build() + ); + mPhoneWindowManager.assertLaunchCategory(category); + } + + mPhoneWindowManager.overrideRoleManager(); + for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) { + final String role = ROLE_SHORTCUTS.valueAt(i); + + mPhoneWindowManager.sendKeyGestureEvent( + new KeyGestureEvent.Builder() + .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) + .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + .setAppLaunchData(AppLaunchData.createLaunchDataForRole(role)) + .build() + ); + mPhoneWindowManager.assertLaunchRole(role); + } + + mPhoneWindowManager.sendKeyGestureEvent( + new KeyGestureEvent.Builder() + .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) + .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + .setAppLaunchData( + new AppLaunchData.ComponentData("com.test", + "com.test.BookmarkTest")) + .build() + ); + mPhoneWindowManager.assertActivityTargetLaunched( + new ComponentName("com.test", "com.test.BookmarkTest")); + + } + /** * ALT + TAB to show recent apps. */ diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index a85f8666d2e1..6596ee935b4b 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -197,7 +197,7 @@ class TestPhoneWindowManager { } @Override - boolean toggleTalkback(int currentUserId) { + boolean toggleTalkback(int currentUserId, ShortcutSource source) { mIsTalkBackEnabled = !mIsTalkBackEnabled; return mIsTalkBackEnabled; } @@ -822,9 +822,9 @@ class TestPhoneWindowManager { void assertTakeBugreport(boolean wasCalled) throws RemoteException { mTestLooper.dispatchAll(); if (wasCalled) { - verify(mActivityManagerService).requestInteractiveBugReport(); + verify(mActivityManagerService).launchBugReportHandlerApp(); } else { - verify(mActivityManagerService, never()).requestInteractiveBugReport(); + verify(mActivityManagerService, never()).launchBugReportHandlerApp(); } } 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 cb17f35f64d7..63dafcd0b5a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java @@ -197,7 +197,7 @@ public class ActivityRefresherTests extends WindowTestsBase { /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) - .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(), + .scheduleTransactionItems(mActivity.app.getThread(), refreshCallbackItem, resumeActivityItem); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index d5f86b6feac8..b25b13f30e20 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1625,16 +1625,18 @@ public class ActivityStarterTests extends WindowTestsBase { assertTrue(activityTop.isVisible()); assertTrue(activityTop.isVisibleRequested()); + final ActivityRecord[] outActivity = new ActivityRecord[1]; final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */); starter.mStartActivity = activityBot; task.inRecents = true; - starter.setInTask(task); + starter.setInTask(task).setOutActivity(outActivity); starter.getIntent().setComponent(activityBot.mActivityComponent); final int result = starter.setReason("testRecordActivityMovement").execute(); assertEquals(START_DELIVERED_TO_TOP, result); assertNotNull(starter.mMovedToTopActivity); + assertEquals(activityBot, outActivity[0]); final ActivityStarter starter2 = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index c176658da847..e0b29c937381 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -382,13 +382,11 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test public void testResumeNextActivityOnCrashedAppDied() { - mSupervisor.beginDeferResume(); final ActivityRecord homeActivity = new ActivityBuilder(mAtm) .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()) .build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity.setState(RESUMED, "test"); - mSupervisor.endDeferResume(); assertEquals(activity.app, mAtm.mInternal.getTopApp()); 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 08963f1c7647..3742249bdffe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java @@ -243,6 +243,10 @@ class AppCompatActivityRobot { doReturn(mTaskStack.top()).when(mActivityStack.top()).getOrganizedTask(); } + void setIsInLetterboxAnimation(boolean inAnimation) { + doReturn(inAnimation).when(mActivityStack.top()).isInLetterboxAnimation(); + } + void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) { doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode(); } @@ -284,6 +288,10 @@ class AppCompatActivityRobot { } } + void setFixedRotationTransformDisplayBounds(@Nullable Rect bounds) { + doReturn(bounds).when(mActivityStack.top()).getFixedRotationTransformDisplayBounds(); + } + void destroyTopActivity() { mActivityStack.top().removeImmediately(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java new file mode 100644 index 000000000000..673d04166a7a --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxUtilsTest.java @@ -0,0 +1,254 @@ +/* + * Copyright 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 com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxInnerBounds; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxOuterBounds; +import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxPosition; + +import static org.mockito.Mockito.mock; + +import android.graphics.Point; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; + +/** + * Tests for the {@link AppCompatLetterboxUtils} class. + * + * Build/Install/Run: + * atest WmTests:AppCompatLetterboxUtilsTest + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatLetterboxUtilsTest extends WindowTestsBase { + + @Test + public void allEmptyWhenIsAppNotLetterboxed() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(false); + robot.getLetterboxPosition(); + robot.assertPosition(/* x */ 0, /* y */0); + robot.getInnerBounds(); + robot.assertInnerBounds(/* left */ 0, /* top */ 0, /* right */ 0, /* bottom */ 0); + robot.getOuterBounds(); + robot.assertOuterBounds(/* left */ 0, /* top */ 0, /* right */ 0, /* bottom */ 0); + }); + } + + @Test + public void positionIsFromTaskWhenLetterboxAnimationIsRunning() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.activity().setIsInLetterboxAnimation(true); + robot.activity().configureTaskBounds( + new Rect(/* left */ 100, /* top */ 200, /* right */ 300, /* bottom */ 400)); + robot.getLetterboxPosition(); + + robot.assertPosition(/* x */ 100, /* y */ 200); + }); + } + + @Test + public void positionIsFromActivityWhenLetterboxAnimationIsNotRunning() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.activity().setIsInLetterboxAnimation(false); + robot.activity().configureTopActivityBounds( + new Rect(/* left */ 200, /* top */ 400, /* right */ 300, /* bottom */ 400)); + robot.getLetterboxPosition(); + + robot.assertPosition(/* x */ 200, /* y */ 400); + }); + } + + @Test + public void outerBoundsWhenFixedRotationTransformDisplayBoundsIsAvailable() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.activity().setFixedRotationTransformDisplayBounds( + new Rect(/* left */ 1, /* top */ 2, /* right */ 3, /* bottom */ 4)); + robot.getOuterBounds(); + + robot.assertOuterBounds(/* left */ 1, /* top */ 2, /* right */ 3, /* bottom */ 4); + }); + } + + @Test + public void outerBoundsNoFixedRotationTransformDisplayBoundsInMultiWindow() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.activity().setFixedRotationTransformDisplayBounds(null); + robot.activity().setTopActivityInMultiWindowMode(true); + robot.getOuterBounds(); + + robot.checkOuterBoundsAreTaskFragmentBounds(); + }); + } + + @Test + public void outerBoundsNoFixedRotationTransformDisplayBoundsNotInMultiWindow() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.activity().setFixedRotationTransformDisplayBounds(null); + robot.activity().setTopActivityInMultiWindowMode(false); + robot.getOuterBounds(); + + robot.checkOuterBoundsAreRootTaskParentBounds(); + }); + } + + @Test + public void innerBoundsTransparencyPolicyIsRunning() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.setTopActivityTransparentPolicyRunning(true); + + robot.getInnerBounds(); + + robot.checkInnerBoundsAreActivityBounds(); + }); + } + + @Test + public void innerBoundsTransparencyPolicyIsNotRunning() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.setTopActivityLetterboxPolicyRunning(true); + robot.setTopActivityTransparentPolicyRunning(false); + robot.setWindowFrame( + new Rect(/* left */ 100, /* top */ 200, /* right */ 300, /* bottom */ 400)); + + robot.getInnerBounds(); + + robot.assertInnerBounds(/* left */ 100, /* top */ 200, /* right */ 300, /* bottom */ + 400); + }); + } + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer<LetterboxUtilsRobotTest> consumer) { + final LetterboxUtilsRobotTest robot = new LetterboxUtilsRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class LetterboxUtilsRobotTest extends AppCompatRobotBase { + + private final Point mPosition = new Point(); + private final Rect mInnerBound = new Rect(); + private final Rect mOuterBound = new Rect(); + + @NonNull + private final WindowState mWindowState; + + LetterboxUtilsRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + mWindowState = mock(WindowState.class); + } + + @Override + void onPostActivityCreation(@NonNull ActivityRecord activity) { + super.onPostActivityCreation(activity); + spyOn(activity.mAppCompatController.getAppCompatLetterboxPolicy()); + spyOn(activity.mAppCompatController.getTransparentPolicy()); + } + + void setTopActivityLetterboxPolicyRunning(boolean running) { + doReturn(running).when(activity().top().mAppCompatController + .getAppCompatLetterboxPolicy()).isRunning(); + } + + void setTopActivityTransparentPolicyRunning(boolean running) { + doReturn(running).when(activity().top().mAppCompatController + .getTransparentPolicy()).isRunning(); + } + + void setWindowFrame(@NonNull Rect frame) { + doReturn(frame).when(mWindowState).getFrame(); + } + + void getLetterboxPosition() { + calculateLetterboxPosition(activity().top(), mPosition); + } + + void getInnerBounds() { + calculateLetterboxInnerBounds(activity().top(), mWindowState, mInnerBound); + } + + void getOuterBounds() { + calculateLetterboxOuterBounds(activity().top(), mOuterBound); + } + + void assertPosition(int expectedX, int expectedY) { + Assert.assertEquals(expectedX, mPosition.x); + Assert.assertEquals(expectedY, mPosition.y); + } + + void assertInnerBounds(int expectedLeft, int expectedTop, int expectedRight, + int expectedBottom) { + Assert.assertEquals(expectedLeft, mInnerBound.left); + Assert.assertEquals(expectedTop, mInnerBound.top); + Assert.assertEquals(expectedRight, mInnerBound.right); + Assert.assertEquals(expectedBottom, mInnerBound.bottom); + } + + void assertOuterBounds(int expectedLeft, int expectedTop, int expectedRight, + int expectedBottom) { + Assert.assertEquals(expectedLeft, mOuterBound.left); + Assert.assertEquals(expectedTop, mOuterBound.top); + Assert.assertEquals(expectedRight, mOuterBound.right); + Assert.assertEquals(expectedBottom, mOuterBound.bottom); + } + + void checkOuterBoundsAreRootTaskParentBounds() { + Assert.assertEquals(mOuterBound, + activity().top().getRootTask().getParent().getBounds()); + } + + void checkOuterBoundsAreTaskFragmentBounds() { + Assert.assertEquals(mOuterBound, + activity().top().getTaskFragment().getBounds()); + } + + void checkInnerBoundsAreActivityBounds() { + Assert.assertEquals(mInnerBound, activity().top().getBounds()); + } + + } +} 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 a07fd235d14f..e447565a55bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java @@ -453,7 +453,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) - .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(), + .scheduleTransactionItems(mActivity.app.getThread(), refreshCallbackItem, resumeActivityItem); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java index 3ddf8da18d16..02ad9dbfda8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java @@ -123,7 +123,7 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase { } @Test - public void testScheduleTransactionItemUnlocked() throws RemoteException { + public void testScheduleTransactionItemNow() throws RemoteException { // Use non binder client to get non-recycled ClientTransaction. mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem); @@ -133,12 +133,12 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase { } @Test - public void testScheduleTransactionAndLifecycleItems() throws RemoteException { + public void testScheduleTransactionItems() throws RemoteException { spyOn(mWms.mWindowPlacerLocked); doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); // Use non binder client to get non-recycled ClientTransaction. - mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, + mLifecycleManager.scheduleTransactionItems(mNonBinderClient, mTransactionItem, mLifecycleItem); assertEquals(1, mLifecycleManager.mPendingTransactions.size()); @@ -155,14 +155,15 @@ public class ClientLifecycleManagerTests extends SystemServiceTestsBase { } @Test - public void testScheduleTransactionAndLifecycleItems_shouldDispatchImmediately() + public void testScheduleTransactionItems_shouldDispatchImmediately() throws RemoteException { spyOn(mWms.mWindowPlacerLocked); doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); // Use non binder client to get non-recycled ClientTransaction. - mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, - mLifecycleItem, true /* shouldDispatchImmediately */); + mLifecycleManager.scheduleTransactionItems(mNonBinderClient, + true /* shouldDispatchImmediately */, + mTransactionItem, mLifecycleItem); verify(mLifecycleManager).scheduleTransaction(any()); assertTrue(mLifecycleManager.mPendingTransactions.isEmpty()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 3bd57475614f..eb8bc9125034 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -40,7 +40,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -141,49 +140,49 @@ public class DisplayPolicyTests extends WindowTestsBase { // If everything is null, return null. assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( - null, null, NAV_BAR_BOTTOM)); + null, null, true)); // If no IME windows, return candidate window. assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( - candidate, null, NAV_BAR_BOTTOM)); + candidate, null, true)); assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingImTarget, null, NAV_BAR_BOTTOM)); + dimmingImTarget, null, true)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingNonImTarget, null, NAV_BAR_BOTTOM)); + dimmingNonImTarget, null, true)); // If IME is not visible, return candidate window. assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( - null, invisibleIme, NAV_BAR_BOTTOM)); + null, invisibleIme, true)); assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( - candidate, invisibleIme, NAV_BAR_BOTTOM)); + candidate, invisibleIme, true)); assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingImTarget, invisibleIme, NAV_BAR_BOTTOM)); + dimmingImTarget, invisibleIme, true)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingNonImTarget, invisibleIme, NAV_BAR_BOTTOM)); + dimmingNonImTarget, invisibleIme, true)); // If IME is visible, return candidate when the candidate window is not dimming. assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - null, visibleIme, NAV_BAR_BOTTOM)); + null, visibleIme, true)); assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - candidate, visibleIme, NAV_BAR_BOTTOM)); + candidate, visibleIme, true)); // If IME is visible and the candidate window is dimming, checks whether the dimming window // can be IME tartget or not. assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + dimmingImTarget, visibleIme, true)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + dimmingNonImTarget, visibleIme, true)); // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color // window. assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( - null, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + null, imeNonDrawNavBar, true)); assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( - candidate, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + candidate, imeNonDrawNavBar, true)); assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + dimmingImTarget, imeNonDrawNavBar, true)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + dimmingNonImTarget, imeNonDrawNavBar, true)); } @Test @@ -196,32 +195,32 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState nonDrawBarIme = createInputMethodWindow(true, false, false); assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( - drawBarWin, null, NAV_BAR_BOTTOM)); + drawBarWin, null, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - null, null, NAV_BAR_BOTTOM)); + null, null, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - nonDrawBarWin, null, NAV_BAR_BOTTOM)); + nonDrawBarWin, null, true)); assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( - drawBarWin, visibleIme, NAV_BAR_BOTTOM)); + drawBarWin, visibleIme, true)); assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( - null, visibleIme, NAV_BAR_BOTTOM)); + null, visibleIme, true)); assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( - nonDrawBarWin, visibleIme, NAV_BAR_BOTTOM)); + nonDrawBarWin, visibleIme, true)); assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( - drawBarWin, invisibleIme, NAV_BAR_BOTTOM)); + drawBarWin, invisibleIme, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - null, invisibleIme, NAV_BAR_BOTTOM)); + null, invisibleIme, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - nonDrawBarWin, invisibleIme, NAV_BAR_BOTTOM)); + nonDrawBarWin, invisibleIme, true)); assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( - drawBarWin, nonDrawBarIme, NAV_BAR_BOTTOM)); + drawBarWin, nonDrawBarIme, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - null, nonDrawBarIme, NAV_BAR_BOTTOM)); + null, nonDrawBarIme, true)); assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( - nonDrawBarWin, nonDrawBarIme, NAV_BAR_BOTTOM)); + nonDrawBarWin, nonDrawBarIme, true)); } @SetupWindows(addWindows = W_NAVIGATION_BAR) 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 f4fa12ea361d..23c767c87e4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -620,7 +620,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { /* isForward */ false, /* shouldSendCompatFakeFocus */ false); verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0)) - .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(), + .scheduleTransactionItems(mActivity.app.getThread(), refreshCallbackItem, resumeActivityItem); } 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 e66dfeb8367c..1c878021c9e9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -2895,7 +2895,7 @@ public class SizeCompatTests extends WindowTestsBase { // Launch another portrait fixed app. spyOn(mTask); setBooted(display.mWmService.mAtmService); - final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService) + final ActivityRecord newActivity = getActivityBuilderWithoutTask() .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) .setTask(mTask) @@ -2963,7 +2963,7 @@ public class SizeCompatTests extends WindowTestsBase { // Launch another portrait fixed app with max aspect ratio as 1.3. spyOn(mTask); setBooted(display.mWmService.mAtmService); - final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService) + final ActivityRecord newActivity = getActivityBuilderWithoutTask() .setResizeMode(RESIZE_MODE_UNRESIZEABLE) .setMaxAspectRatio(1.3f) .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) @@ -4788,7 +4788,7 @@ public class SizeCompatTests extends WindowTestsBase { final float maxAspect = 1.8f; final float minAspect = 1.5f; prepareLimitedBounds(mActivity, maxAspect, minAspect, - ActivityInfo.SCREEN_ORIENTATION_LOCKED, true /* isUnresizable */); + ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, true /* isUnresizable */); assertTrue(mActivity.isUniversalResizeable()); assertTrue(mActivity.isResizeable()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index 00ecd008cde7..546b1ba66b08 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -19,11 +19,9 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.Surface.ROTATION_0; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -208,7 +206,7 @@ class TestDisplayContent extends DisplayContent { if (mSystemDecorations) { doReturn(true).when(newDisplay).supportsSystemDecorations(); doReturn(true).when(displayPolicy).hasNavigationBar(); - doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt()); + doReturn(true).when(displayPolicy).hasBottomNavigationBar(); } else { doReturn(false).when(displayPolicy).hasNavigationBar(); doReturn(false).when(displayPolicy).hasStatusBar(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 9bad2ec2ca2a..1dfb20a41816 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -309,6 +309,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { newConfig.densityDpi += 100; mWpc.onConfigurationChanged(newConfig); verify(mClientLifecycleManager, never()).scheduleTransactionItem(eq(thread), any()); + verify(mClientLifecycleManager, never()).scheduleTransactionItems(eq(thread), any()); verify(mClientLifecycleManager, never()).scheduleTransactionItemNow(eq(thread), any()); // Cached -> non-cached will send the previous deferred config immediately. diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index f8037175fb05..7adcd4675297 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -101,7 +101,7 @@ public abstract class Conference extends Conferenceable { private Set<String> mPreviousExtraKeys; private final Object mExtrasLock = new Object(); private Uri mAddress; - private int mAddressPresentation; + private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN; private String mCallerDisplayName; private int mCallerDisplayNamePresentation; private int mCallDirection; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index ad7d9870ca98..29d394201f39 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -2164,7 +2164,7 @@ public abstract class Connection extends Conferenceable { private CallAudioState mCallAudioState; private CallEndpoint mCallEndpoint; private Uri mAddress; - private int mAddressPresentation; + private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN; private String mCallerDisplayName; private int mCallerDisplayNamePresentation; private boolean mRingbackRequested = false; diff --git a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl index 50e3a0e4a79d..cfa06ee1bd51 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteModemStateCallback.aidl @@ -42,4 +42,11 @@ oneway interface ISatelliteModemStateCallback { * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 */ void onRegistrationFailure(in int causeCode); + + /** + * Indicates that the background search for terrestrial network is finished with result + * + * @param isAvailable True means there's terrestrial network and false means there's not. + */ + void onTerrestrialNetworkAvailableChanged(in boolean isAvailable); } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 44de65a009ff..f0c3504c27ef 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -127,7 +127,9 @@ public final class SatelliteManager { /** * Exception from the satellite service containing the {@link SatelliteResult} error code. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static class SatelliteException extends Exception { @SatelliteResult private final int mErrorCode; @@ -257,140 +259,210 @@ public final class SatelliteManager { /** * The request was successfully processed. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_SUCCESS = 0; + /** * A generic error which should be used only when other specific errors cannot be used. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_ERROR = 1; + /** * Error received from the satellite server. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_SERVER_ERROR = 2; + /** * Error received from the vendor service. This generic error code should be used * only when the error cannot be mapped to other specific service error codes. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_SERVICE_ERROR = 3; + /** * Error received from satellite modem. This generic error code should be used only when * the error cannot be mapped to other specific modem error codes. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_MODEM_ERROR = 4; + /** * Error received from the satellite network. This generic error code should be used only when * the error cannot be mapped to other specific network error codes. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NETWORK_ERROR = 5; + /** * Telephony is not in a valid state to receive requests from clients. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; + /** * Satellite modem is not in a valid state to receive requests from clients. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7; + /** * Either vendor service, or modem, or Telephony framework has received a request with * invalid arguments from its clients. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8; + /** * Telephony framework failed to send a request or receive a response from the vendor service * or satellite modem due to internal error. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_REQUEST_FAILED = 9; + /** * Radio did not start or is resetting. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10; + /** * The request is not supported by either the satellite modem or the network. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_REQUEST_NOT_SUPPORTED = 11; + /** * Satellite modem or network has no resources available to handle requests from clients. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NO_RESOURCES = 12; + /** * Satellite service is not provisioned yet. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_SERVICE_NOT_PROVISIONED = 13; + /** * Satellite service provision is already in progress. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS = 14; + /** * The ongoing request was aborted by either the satellite modem or the network. * This error is also returned when framework decides to abort current send request as one * of the previous send request failed. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15; + /** * The device/subscriber is barred from accessing the satellite service. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_ACCESS_BARRED = 16; + /** * Satellite modem timeout to receive ACK or response from the satellite network after * sending a request to the network. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17; + /** * Satellite network is not reachable from the modem. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NOT_REACHABLE = 18; + /** * The device/subscriber is not authorized to register with the satellite service provider. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19; + /** * The device does not support satellite. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20; /** * The current request is already in-progress. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_REQUEST_IN_PROGRESS = 21; /** * Satellite modem is currently busy due to which current request cannot be processed. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_MODEM_BUSY = 22; /** * Telephony process is not currently available or satellite is not supported. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23; /** * Telephony framework timeout to receive ACK or response from the satellite modem after * sending a request to the modem. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; @@ -475,27 +547,41 @@ public final class SatelliteManager { /** * Unknown Non-Terrestrial radio technology. This generic radio technology should be used * only when the radio technology cannot be mapped to other specific radio technologies. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; + /** * 3GPP NB-IoT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1; + /** * 3GPP 5G NR over Non-Terrestrial-Networks technology. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; + /** * 3GPP eMTC (enhanced Machine-Type Communication) over Non-Terrestrial-Networks technology. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3; + /** * Proprietary technology. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; @@ -510,16 +596,35 @@ public final class SatelliteManager { @Retention(RetentionPolicy.SOURCE) public @interface NTRadioTechnology {} - /** Suggested device hold position is unknown. */ + /** + * Suggested device hold position is unknown. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0; - /** User is suggested to hold the device in portrait mode. */ + + /** + * User is suggested to hold the device in portrait mode. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1; - /** User is suggested to hold the device in landscape mode with left hand. */ + + /** + * User is suggested to hold the device in landscape mode with left hand. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; - /** User is suggested to hold the device in landscape mode with right hand. */ + + /** + * User is suggested to hold the device in landscape mode with right hand. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3; @@ -533,18 +638,37 @@ public final class SatelliteManager { @Retention(RetentionPolicy.SOURCE) public @interface DeviceHoldPosition {} - /** Display mode is unknown. */ + /** + * Display mode is unknown. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DISPLAY_MODE_UNKNOWN = 0; - /** Display mode of the device used for satellite communication for non-foldable phones. */ + + /** + * Display mode of the device used for satellite communication for non-foldable phones. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DISPLAY_MODE_FIXED = 1; - /** Display mode of the device used for satellite communication for foldabale phones when the - * device is opened. */ + + /** + * Display mode of the device used for satellite communication for foldabale phones when the + * device is opened. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DISPLAY_MODE_OPENED = 2; - /** Display mode of the device used for satellite communication for foldabable phones when the - * device is closed. */ + + /** + * Display mode of the device used for satellite communication for foldabable phones when the + * device is closed. + * @hide + */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DISPLAY_MODE_CLOSED = 3; @@ -561,13 +685,18 @@ public final class SatelliteManager { /** * The emergency call is handed over to oem-enabled satellite SOS messaging. SOS messages are * sent to SOS providers, which will then forward the messages to emergency providers. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS = 1; + /** * The emergency call is handed over to carrier-enabled satellite T911 messaging. T911 messages * are sent directly to local emergency providers. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2; @@ -582,6 +711,22 @@ public final class SatelliteManager { "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED"; /** + * Meta-data represents whether the application supports P2P SMS over carrier roaming satellite + * which needs manual trigger to connect to satellite. The messaging applications that supports + * P2P SMS over carrier roaming satellites should add the following in their AndroidManifest. + * {@code + * <application + * <meta-data + * android:name="android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT" + * android:value="true"/> + * </application> + * } + * @hide + */ + public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT = + "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT"; + + /** * Request to enable or disable the satellite modem and demo mode. * If satellite modem and cellular modem cannot work concurrently, * then this will disable the cellular modem if satellite modem is enabled, @@ -598,7 +743,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestEnabled(@NonNull EnableRequestAttributes attributes, @@ -644,7 +792,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsEnabled(@NonNull @CallbackExecutor Executor executor, @@ -701,7 +852,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor, @@ -758,7 +912,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsEmergencyModeEnabled(@NonNull @CallbackExecutor Executor executor, @@ -816,7 +973,10 @@ public final class SatelliteManager { * service is supported on the device and {@code false} otherwise. * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} * will return a {@link SatelliteException} with the {@link SatelliteResult}. + * + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsSupported(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) { @@ -871,7 +1031,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, @@ -920,56 +1083,80 @@ public final class SatelliteManager { /** * The default state indicating that datagram transfer is idle. * This should be sent if there are no message transfer activity happening. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; + /** * A transition state indicating that a datagram is being sent. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1; + /** * An end state indicating that datagram sending completed successfully. * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE} * will be sent if no more messages are pending. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; + /** * An end state indicating that datagram sending completed with a failure. * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE} * must be sent before reporting any additional datagram transfer state changes. All pending * messages will be reported as failed, to the corresponding applications. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; + /** * A transition state indicating that a datagram is being received. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4; + /** * An end state indicating that datagram receiving completed successfully. * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE} * will be sent if no more messages are pending. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5; + /** * An end state indicating that datagram receive operation found that there are no * messages to be retrieved from the satellite. * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE} * will be sent if no more messages are pending. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; + /** * An end state indicating that datagram receive completed with a failure. * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE} * will be sent if no more messages are pending. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; + /** * A transition state indicating that Telephony is waiting for satellite modem to connect to a * satellite network before sending a datagram or polling for datagrams. If the satellite modem @@ -978,14 +1165,19 @@ public final class SatelliteManager { * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING} will be sent. Otherwise, * either {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED} or * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED} will be sent. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; + /** * The datagram transfer state is unknown. This generic datagram transfer state should be used * only when the datagram transfer state cannot be mapped to other specific datagram transfer * states. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; @@ -1008,58 +1200,86 @@ public final class SatelliteManager { /** * Satellite modem is in idle state. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_IDLE = 0; + /** * Satellite modem is listening for incoming datagrams. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_LISTENING = 1; + /** * Satellite modem is sending and/or receiving datagrams. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; + /** * Satellite modem is retrying to send and/or receive datagrams. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; + /** * Satellite modem is powered off. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_OFF = 4; + /** * Satellite modem is unavailable. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; + /** * The satellite modem is powered on but the device is not registered to a satellite cell. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; + /** * The satellite modem is powered on and the device is registered to a satellite cell. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; + /** * The satellite modem is being powered on. * @hide */ public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8; + /** * The satellite modem is being powered off. * @hide */ public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9; + /** * Satellite modem state is unknown. This generic modem state should be used only when the * modem state cannot be mapped to other specific modem states. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; @@ -1083,43 +1303,56 @@ public final class SatelliteManager { /** * Datagram type is unknown. This generic datagram type should be used only when the * datagram type cannot be mapped to other specific datagram types. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DATAGRAM_TYPE_UNKNOWN = 0; + /** * Datagram type indicating that the datagram to be sent or received is of type SOS message. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; + /** * Datagram type indicating that the datagram to be sent or received is of type * location sharing. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; + /** * This type of datagram is used to keep the device in satellite connected state or check if * there is any incoming message. * @hide */ public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; + /** * Datagram type indicating that the datagram to be sent or received is of type SOS message and * is the last message to emergency service provider indicating still needs help. * @hide */ public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4; + /** * Datagram type indicating that the datagram to be sent or received is of type SOS message and * is the last message to emergency service provider indicating no more help is needed. * @hide */ public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; + /** * Datagram type indicating that the message to be sent or received is of type SMS. * @hide */ public static final int DATAGRAM_TYPE_SMS = 6; + /** * Datagram type indicating that the message to be sent is an SMS checking * for pending incoming SMS. @@ -1150,7 +1383,9 @@ public final class SatelliteManager { /** * Satellite communication restricted by geolocation. This can be * triggered based upon geofence input provided by carrier to enable or disable satellite. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; @@ -1158,7 +1393,9 @@ public final class SatelliteManager { * Satellite communication restricted by entitlement server. This can be triggered based on * the EntitlementStatus value received from the entitlement server to enable or disable * satellite. + * @hide */ + @SystemApi @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2; @@ -1185,7 +1422,10 @@ public final class SatelliteManager { * @param callback The callback to notify of satellite transmission updates. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SuppressWarnings("SamShouldBeLast") @@ -1268,7 +1508,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void stopTransmissionUpdates(@NonNull SatelliteTransmissionUpdateCallback callback, @@ -1326,7 +1569,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void provisionService(@NonNull String token, @NonNull byte[] provisionData, @@ -1381,7 +1627,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void deprovisionService(@NonNull String token, @@ -1424,7 +1673,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForProvisionStateChanged( @@ -1476,7 +1728,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void unregisterForProvisionStateChanged( @@ -1514,7 +1769,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsProvisioned(@NonNull @CallbackExecutor Executor executor, @@ -1569,7 +1827,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForModemStateChanged( @@ -1601,6 +1862,12 @@ public final class SatelliteManager { executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onRegistrationFailure(causeCode))); } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + executor.execute(() -> Binder.withCleanCallingIdentity(() -> + callback.onTerrestrialNetworkAvailableChanged(isAvailable))); + } }; sSatelliteModemStateCallbackMap.put(callback, internalCallback); return telephony.registerForSatelliteModemStateChanged(internalCallback); @@ -1623,7 +1890,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void unregisterForModemStateChanged( @@ -1663,7 +1933,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForIncomingDatagram( @@ -1719,7 +1992,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void unregisterForIncomingDatagram(@NonNull SatelliteDatagramCallback callback) { @@ -1757,7 +2033,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void pollPendingDatagrams(@NonNull @CallbackExecutor Executor executor, @@ -1812,7 +2091,10 @@ public final class SatelliteManager { * @param resultListener Listener for the {@link SatelliteResult} result of the operation. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void sendDatagram(@DatagramType int datagramType, @@ -1860,7 +2142,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestIsCommunicationAllowedForCurrentLocation( @@ -1918,7 +2203,10 @@ public final class SatelliteManager { * will return a {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor, @@ -1976,7 +2264,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void setDeviceAlignedWithSatellite(boolean isAligned) { @@ -2016,7 +2307,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public void requestAttachEnabledForCarrier(int subId, boolean enableSatellite, @@ -2050,7 +2344,10 @@ public final class SatelliteManager { * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public void requestIsAttachEnabledForCarrier(int subId, @@ -2075,7 +2372,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public void addAttachRestrictionForCarrier(int subId, @@ -2120,7 +2420,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) public void removeAttachRestrictionForCarrier(int subId, @@ -2164,7 +2467,10 @@ public final class SatelliteManager { * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if the subscription is invalid. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @SatelliteCommunicationRestrictionReason @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) @@ -2214,7 +2520,10 @@ public final class SatelliteManager { * {@link SatelliteException} with the {@link SatelliteResult}. * * @throws SecurityException if the caller doesn't have required permission. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor, @@ -2277,7 +2586,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void registerForNtnSignalStrengthChanged(@NonNull @CallbackExecutor Executor executor, @@ -2326,7 +2638,10 @@ public final class SatelliteManager { * @throws IllegalArgumentException if the callback is not valid or has already been * unregistered. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void unregisterForNtnSignalStrengthChanged(@NonNull NtnSignalStrengthCallback callback) { @@ -2360,7 +2675,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @SatelliteResult public int registerForCapabilitiesChanged( @@ -2403,7 +2721,10 @@ public final class SatelliteManager { * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public void unregisterForCapabilitiesChanged( @@ -2436,7 +2757,10 @@ public final class SatelliteManager { * * @return List of plmn for carrier satellite service. If no plmn is available, empty list will * be returned. + * + * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) @NonNull public List<String> getSatellitePlmnsForCarrier(int subId) { diff --git a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java index 13af4694389b..040fbbbb2689 100644 --- a/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteModemStateCallback.java @@ -54,4 +54,12 @@ public interface SatelliteModemStateCallback { * @hide */ default void onRegistrationFailure(int causeCode) {}; + + /** + * Indicates that the background search for terrestrial network is finished with result + * + * @param isAvailable True means there's terrestrial network and false means there's not. + * @hide + */ + default void onTerrestrialNetworkAvailableChanged(boolean isAvailable) {}; } diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl index 8b51321bc7c4..ac0a5fc320c6 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl @@ -70,12 +70,16 @@ oneway interface ISatellite { in IIntegerConsumer resultCallback); /** - * Allow cellular modem scanning while satellite mode is on. + * Framework will call this API to determine if there are any TN networks available. + * This API will be called if framework identifies that user is inactive i.e. no + * data transfer or no pointing to satellite. + * Modem can send the callback with available for either in service or limited service. + * @param enabled {@code true} to enable cellular modem while satellite mode is on * and {@code false} to disable * @param errorCallback The callback to receive the error code result of the operation. */ - void enableCellularModemWhileSatelliteModeIsOn(in boolean enabled, + void enableTerrestrialNetworkScanWhileSatelliteModeIsOn(in boolean enabled, in IIntegerConsumer errorCallback); /** diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl index 3f2fce2b9f1d..a2ebd3a11750 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl @@ -82,4 +82,11 @@ oneway interface ISatelliteListener { * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 */ void onRegistrationFailure(in int causeCode); + + /** + * Modem can send the callback with available for either in service or limited service. + * + * @param isAvailable True means there's terrestrial network and false means there's not. + */ + void onTerrestrialNetworkAvailableChanged(in boolean isAvailable); } diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index 4f472106a329..eaeed2a0a9fa 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -80,12 +80,12 @@ public class SatelliteImplBase extends SatelliteService { } @Override - public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled, + public void enableTerrestrialNetworkScanWhileSatelliteModeIsOn(boolean enabled, IIntegerConsumer resultCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .enableCellularModemWhileSatelliteModeIsOn(enabled, resultCallback), - "enableCellularModemWhileSatelliteModeIsOn"); + .enableTerrestrialNetworkScanWhileSatelliteModeIsOn( + enabled, resultCallback), "enableTerrestrialNetworkScanWhileSatelliteModeIsOn"); } @Override @@ -314,7 +314,7 @@ public class SatelliteImplBase extends SatelliteService { * and {@code false} to disable * @param resultCallback The callback to receive the error code result of the operation. */ - public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled, + public void enableTerrestrialNetworkScanWhileSatelliteModeIsOn(boolean enabled, @NonNull IIntegerConsumer resultCallback) { // stub implementation } diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp index 38cb9869f165..e14e5fea001f 100644 --- a/tests/BinaryTransparencyHostTest/Android.bp +++ b/tests/BinaryTransparencyHostTest/Android.bp @@ -32,7 +32,7 @@ java_test_host { static_libs: [ "truth", ], - data: [ + device_common_data: [ ":BinaryTransparencyTestApp", ":EasterEgg", ":FeatureSplitBase", diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp index 37cb8500fbab..a0e047759dab 100644 --- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp +++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp @@ -36,7 +36,7 @@ python_test_host { unit_test: false, tags: ["mobly"], }, - data: [ + device_common_data: [ ":cdm_snippet_legacy", ], version: { diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp index 3f2c80831565..45bbcb434da5 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp @@ -55,6 +55,8 @@ android_test { java_resources: [ ":DynamicCodeLoggerTestLibrary", + ], + device_first_java_resources: [ ":DynamicCodeLoggerNativeExecutable", ], } diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index c6855b4a97b4..4ac567cc3937 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -285,7 +285,11 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : val displayRect = getDisplayRect(wmHelper) - val endX = if (isLeft) displayRect.left else displayRect.right + val endX = if (isLeft) { + displayRect.left + SNAP_RESIZE_DRAG_INSET + } else { + displayRect.right - SNAP_RESIZE_DRAG_INSET + } val endY = displayRect.centerY() / 2 // drag the window to snap resize @@ -391,6 +395,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : private companion object { val TIMEOUT: Duration = Duration.ofSeconds(3) + const val SNAP_RESIZE_DRAG_INSET: Int = 5 // inset to avoid dragging to display edge const val CAPTION: String = "desktop_mode_caption" const val MAXIMIZE_BUTTON_VIEW: String = "maximize_button_view" const val MAXIMIZE_MENU: String = "maximize_menu" diff --git a/tests/FsVerityTest/Android.bp b/tests/FsVerityTest/Android.bp index 02268c37a5a3..c2dfa0fffb3b 100644 --- a/tests/FsVerityTest/Android.bp +++ b/tests/FsVerityTest/Android.bp @@ -43,7 +43,7 @@ java_test_host { data_device_bins_both: [ "block_device_writer", ], - data: [ + device_common_data: [ ":FsVerityTestApp", ], } diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp index a5c6d6513f50..9f32d4628769 100644 --- a/tests/OdmApps/Android.bp +++ b/tests/OdmApps/Android.bp @@ -26,7 +26,7 @@ java_test_host { srcs: ["src/**/*.java"], libs: ["tradefed"], test_suites: ["device-tests"], - data: [ + device_common_data: [ ":TestOdmApp", ":TestOdmPrivApp", ], diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 21007ef1396f..766ff4a727bd 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -26,7 +26,11 @@ android_test { manifest: "RollbackTest/AndroidManifest.xml", platform_apis: true, srcs: ["RollbackTest/src/**/*.java"], - static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], + static_libs: [ + "androidx.test.rules", + "cts-rollback-lib", + "cts-install-lib", + ], test_suites: ["general-tests"], test_config: "RollbackTest.xml", java_resources: [ @@ -48,7 +52,7 @@ java_test_host { ], test_suites: ["general-tests"], test_config: "StagedRollbackTest.xml", - data: [ + device_common_data: [ ":com.android.apex.apkrollback.test_v1", ":test.rebootless_apex_v1", ":RollbackTest", @@ -59,10 +63,13 @@ java_test_host { name: "NetworkStagedRollbackTest", srcs: ["NetworkStagedRollbackTest/src/**/*.java"], libs: ["tradefed"], - static_libs: ["RollbackTestLib", "frameworks-base-hostutils"], + static_libs: [ + "RollbackTestLib", + "frameworks-base-hostutils", + ], test_suites: ["general-tests"], test_config: "NetworkStagedRollbackTest.xml", - data: [":RollbackTest"], + device_common_data: [":RollbackTest"], } java_test_host { @@ -74,7 +81,7 @@ java_test_host { ], test_suites: ["general-tests"], test_config: "MultiUserRollbackTest.xml", - data : [":RollbackTest"], + device_common_data: [":RollbackTest"], } java_library_host { @@ -84,55 +91,55 @@ java_library_host { } genrule { - name: "com.android.apex.apkrollback.test.pem", - out: ["com.android.apex.apkrollback.test.pem"], - cmd: "openssl genrsa -out $(out) 4096", + name: "com.android.apex.apkrollback.test.pem", + out: ["com.android.apex.apkrollback.test.pem"], + cmd: "openssl genrsa -out $(out) 4096", } genrule { - name: "com.android.apex.apkrollback.test.pubkey", - srcs: [":com.android.apex.apkrollback.test.pem"], - out: ["com.android.apex.apkrollback.test.pubkey"], - tools: ["avbtool"], - cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)", + name: "com.android.apex.apkrollback.test.pubkey", + srcs: [":com.android.apex.apkrollback.test.pem"], + out: ["com.android.apex.apkrollback.test.pubkey"], + tools: ["avbtool"], + cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)", } apex_key { - name: "com.android.apex.apkrollback.test.key", - private_key: ":com.android.apex.apkrollback.test.pem", - public_key: ":com.android.apex.apkrollback.test.pubkey", - installable: false, + name: "com.android.apex.apkrollback.test.key", + private_key: ":com.android.apex.apkrollback.test.pem", + public_key: ":com.android.apex.apkrollback.test.pubkey", + installable: false, } apex { - name: "com.android.apex.apkrollback.test_v1", - manifest: "testdata/manifest_v1.json", - androidManifest: "testdata/AndroidManifest.xml", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.apkrollback.test.key", - apps: ["TestAppAv1"], - installable: false, - updatable: false, + name: "com.android.apex.apkrollback.test_v1", + manifest: "testdata/manifest_v1.json", + androidManifest: "testdata/AndroidManifest.xml", + file_contexts: ":apex.test-file_contexts", + key: "com.android.apex.apkrollback.test.key", + apps: ["TestAppAv1"], + installable: false, + updatable: false, } apex { - name: "com.android.apex.apkrollback.test_v2", - manifest: "testdata/manifest_v2.json", - androidManifest: "testdata/AndroidManifest.xml", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.apkrollback.test.key", - apps: ["TestAppAv2"], - installable: false, - updatable: false, + name: "com.android.apex.apkrollback.test_v2", + manifest: "testdata/manifest_v2.json", + androidManifest: "testdata/AndroidManifest.xml", + file_contexts: ":apex.test-file_contexts", + key: "com.android.apex.apkrollback.test.key", + apps: ["TestAppAv2"], + installable: false, + updatable: false, } apex { - name: "com.android.apex.apkrollback.test_v2Crashing", - manifest: "testdata/manifest_v2.json", - androidManifest: "testdata/AndroidManifest.xml", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.apkrollback.test.key", - apps: ["TestAppACrashingV2"], - installable: false, - updatable: false, + name: "com.android.apex.apkrollback.test_v2Crashing", + manifest: "testdata/manifest_v2.json", + androidManifest: "testdata/AndroidManifest.xml", + file_contexts: ":apex.test-file_contexts", + key: "com.android.apex.apkrollback.test.key", + apps: ["TestAppACrashingV2"], + installable: false, + updatable: false, } diff --git a/tests/SharedLibraryLoadingTest/Android.bp b/tests/SharedLibraryLoadingTest/Android.bp index 088278d6ee89..8027519b95fb 100644 --- a/tests/SharedLibraryLoadingTest/Android.bp +++ b/tests/SharedLibraryLoadingTest/Android.bp @@ -28,7 +28,7 @@ java_test_host { "junit", ], test_suites: ["general-tests"], - data: [ + device_common_data: [ ":SharedLibraryLoadingTests_StandardSharedLibrary", ":SharedLibraryLoadingTests_SharedLibraryLoadedAfter", ":SharedLibraryLoadingTests_SharedLibraryClientTests", diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 27511411c97e..451870ee4c9a 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -55,7 +55,7 @@ java_test_host { "frameworks-base-hostutils", "cts-install-lib-host", ], - data: [ + device_common_data: [ ":StagedInstallInternalTestApp", ":apex.apexd_test", ":com.android.apex.apkrollback.test_v1", diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp index cc8bc45a7411..153569746cd2 100644 --- a/tests/SystemMemoryTest/host/Android.bp +++ b/tests/SystemMemoryTest/host/Android.bp @@ -26,7 +26,7 @@ java_test_host { srcs: ["src/**/*.java"], libs: ["tradefed"], test_suites: ["general-tests"], - data: [ + device_common_data: [ ":SystemMemoryTestDevice", ], } diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java index 6f3deab1d4fa..2692e12c57ed 100644 --- a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java @@ -40,7 +40,6 @@ import android.tools.traces.io.ResultWriter; import android.tools.traces.monitors.PerfettoTraceMonitor; import android.tools.traces.protolog.ProtoLogTrace; import android.tracing.perfetto.DataSource; -import android.util.proto.ProtoInputStream; import androidx.test.platform.app.InstrumentationRegistry; @@ -74,7 +73,7 @@ import java.util.concurrent.atomic.AtomicInteger; @SuppressWarnings("ConstantConditions") @Presubmit @RunWith(JUnit4.class) -public class PerfettoProtoLogImplTest { +public class ProcessedPerfettoProtoLogImplTest { private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb"; private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation() @@ -100,7 +99,7 @@ public class PerfettoProtoLogImplTest { private static ProtoLogViewerConfigReader sReader; - public PerfettoProtoLogImplTest() throws IOException { + public ProcessedPerfettoProtoLogImplTest() throws IOException { } @BeforeClass @@ -151,7 +150,8 @@ public class PerfettoProtoLogImplTest { ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock( ViewerConfigInputStreamProvider.class); Mockito.when(viewerConfigInputStreamProvider.getInputStream()) - .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray())); + .thenAnswer(it -> new AutoClosableProtoInputStream( + sViewerConfigBuilder.build().toByteArray())); sCacheUpdater = () -> {}; sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); @@ -165,21 +165,16 @@ public class PerfettoProtoLogImplTest { throw new RuntimeException( "Unexpected viewer config file path provided"); } - return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()); + return new AutoClosableProtoInputStream(sViewerConfigBuilder.build().toByteArray()); }); }; sProtoLogConfigurationService = new ProtoLogConfigurationServiceImpl(dataSourceBuilder, tracer); - if (android.tracing.Flags.clientSideProtoLogging()) { - sProtoLog = new PerfettoProtoLogImpl( - MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } else { - sProtoLog = new PerfettoProtoLogImpl( - viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } + sProtoLog = new ProcessedPerfettoProtoLogImpl( + MOCK_VIEWER_CONFIG_FILE, viewerConfigInputStreamProvider, sReader, + () -> sCacheUpdater.run(), TestProtoLogGroup.values(), dataSourceBuilder, + sProtoLogConfigurationService); busyWaitForDataSourceRegistration(TEST_PROTOLOG_DATASOURCE_NAME); } @@ -398,18 +393,17 @@ public class PerfettoProtoLogImplTest { } @Test - public void log_logcatEnabledNoMessage() { + public void log_logcatEnabledNoMessageThrows() { when(sReader.getViewerString(anyLong())).thenReturn(null); PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)")); - verify(sReader).getViewerString(eq(1234L)); + var assertion = assertThrows(RuntimeException.class, () -> + implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5})); + Truth.assertThat(assertion).hasMessageThat() + .contains("Failed to decode message for logcat"); } @Test @@ -539,16 +533,12 @@ public class PerfettoProtoLogImplTest { PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) .build(); - long before; - long after; try { traceMonitor.start(); - before = SystemClock.elapsedRealtimeNanos(); sProtoLog.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, 0b01100100, new Object[]{"test", 1, 0.1, true}); - after = SystemClock.elapsedRealtimeNanos(); } finally { traceMonitor.stop(mWriter); } @@ -606,7 +596,8 @@ public class PerfettoProtoLogImplTest { Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java"); Truth.assertThat(stacktrace) .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java"); - Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName()); + Truth.assertThat(stacktrace) + .contains(ProcessedPerfettoProtoLogImplTest.class.getSimpleName()); Truth.assertThat(stacktrace).contains("stackTraceTrimmed"); } diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java index 8ecddaa76216..3d1e208189b0 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java @@ -47,12 +47,12 @@ public class ProtoLogTest { } @Test - public void throwOnRegisteringDuplicateGroup() { - final var assertion = assertThrows(RuntimeException.class, - () -> ProtoLog.init(TEST_GROUP_1, TEST_GROUP_1, TEST_GROUP_2)); + public void deduplicatesRegisteringDuplicateGroup() { + ProtoLog.init(TEST_GROUP_1, TEST_GROUP_1, TEST_GROUP_2); - Truth.assertThat(assertion).hasMessageThat().contains("" + TEST_GROUP_1.getId()); - Truth.assertThat(assertion).hasMessageThat().contains("duplicate"); + final var instance = ProtoLog.getSingleInstance(); + Truth.assertThat(instance.getRegisteredGroups()) + .containsExactly(TEST_GROUP_1, TEST_GROUP_2); } @Test diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java index d78ced161eaf..9e029a8d5e57 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java @@ -19,9 +19,12 @@ package com.android.internal.protolog; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import android.os.Build; import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; +import com.google.common.truth.Truth; + +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +32,8 @@ import org.junit.runners.JUnit4; import perfetto.protos.ProtologCommon; +import java.io.File; + @Presubmit @RunWith(JUnit4.class) public class ProtoLogViewerConfigReaderTest { @@ -83,7 +88,7 @@ public class ProtoLogViewerConfigReaderTest { ).build().toByteArray(); private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider = - () -> new ProtoInputStream(TEST_VIEWER_CONFIG); + () -> new AutoClosableProtoInputStream(TEST_VIEWER_CONFIG); private ProtoLogViewerConfigReader mConfig; @@ -123,6 +128,31 @@ public class ProtoLogViewerConfigReaderTest { } @Test + public void viewerConfigIsOnDevice() { + Assume.assumeFalse(Build.FINGERPRINT.contains("robolectric")); + + final String[] viewerConfigPaths; + if (android.tracing.Flags.perfettoProtologTracing()) { + viewerConfigPaths = new String[] { + "/system_ext/etc/wmshell.protolog.pb", + "/system/etc/core.protolog.pb", + }; + } else { + viewerConfigPaths = new String[] { + "/system_ext/etc/wmshell.protolog.json.gz", + "/system/etc/protolog.conf.json.gz", + }; + } + + for (final var viewerConfigPath : viewerConfigPaths) { + File f = new File(viewerConfigPath); + + Truth.assertWithMessage(f.getAbsolutePath() + " exists").that(f.exists()).isTrue(); + } + + } + + @Test public void loadUnloadAndReloadViewerConfig() { loadViewerConfig(); unloadViewerConfig(); diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index 006a02908643..3bde92909cd5 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -10,6 +10,14 @@ from xml.etree import ElementTree from fontTools import ttLib +# TODO(nona): Remove hard coded font version and unicode versions. +# Figure out a way of giving this information with command lines. +EMOJI_FONT_TO_UNICODE_MAP = { + '2.034': 15.0, + '2.042': 15.1, + '2.047': 16.0, +} + EMOJI_VS = 0xFE0F LANG_TO_SCRIPT = { @@ -217,9 +225,8 @@ def check_hyphens(hyphens_dir): class FontRecord(object): - def __init__(self, name, psName, scripts, variant, weight, style, fallback_for, font): + def __init__(self, name, scripts, variant, weight, style, fallback_for, font): self.name = name - self.psName = psName self.scripts = scripts self.variant = variant self.weight = weight @@ -282,13 +289,23 @@ def parse_fonts_xml(fonts_xml_path): m = trim_re.match(font_file) font_file = m.group(1) - weight = int(child.get('weight')) - assert weight % 100 == 0, ( - 'Font weight "%d" is not a multiple of 100.' % weight) - - style = child.get('style') - assert style in {'normal', 'italic'}, ( - 'Unknown style "%s"' % style) + # In case of variable font and it supports `wght` axis, the weight attribute can be + # dropped which is automatically adjusted at runtime. + if 'weight' in child: + weight = int(child.get('weight')) + assert weight % 100 == 0, ( + 'Font weight "%d" is not a multiple of 100.' % weight) + else: + weight = None + + # In case of variable font and it supports `ital` or `slnt` axes, the style attribute + # can be dropped which is automatically adjusted at runtime. + if 'style' in child: + style = child.get('style') + assert style in {'normal', 'italic'}, ( + 'Unknown style "%s"' % style) + else: + style = None fallback_for = child.get('fallbackFor') @@ -306,7 +323,6 @@ def parse_fonts_xml(fonts_xml_path): record = FontRecord( name, - child.get('postScriptName'), frozenset(scripts), variant, weight, @@ -357,6 +373,11 @@ def is_regional_indicator(x): # regional indicator A..Z return 0x1F1E6 <= x <= 0x1F1FF +def is_flag_sequence(seq): + if type(seq) == int: + return False + len(seq) == 2 and is_regional_indicator(seq[0]) and is_regional_indicator(seq[1]) + def is_tag(x): # tag block return 0xE0000 <= x <= 0xE007F @@ -391,17 +412,43 @@ def check_emoji_not_compat(all_emoji, equivalent_emoji): if "meta" in ttf: assert 'Emji' not in ttf["meta"].data, 'NotoColorEmoji MUST be a compat font' +def is_flag_emoji(font): + return 0x1F1E6 in get_best_cmap(font) + +def emoji_font_version_to_unicode_version(font_version): + version_str = '%.3f' % font_version + assert version_str in EMOJI_FONT_TO_UNICODE_MAP, 'Unknown emoji font verion: %s' % version_str + return EMOJI_FONT_TO_UNICODE_MAP[version_str] + def check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji): coverages = [] + emoji_font_version = 0 + emoji_flag_font_version = 0 for emoji_font in emoji_fonts: coverages.append(get_emoji_map(emoji_font)) + # Find the largest version of the installed emoji font. + version = open_font(emoji_font)['head'].fontRevision + if is_flag_emoji(emoji_font): + emoji_flag_font_version = max(emoji_flag_font_version, version) + else: + emoji_font_version = max(emoji_font_version, version) + + emoji_flag_unicode_version = emoji_font_version_to_unicode_version(emoji_flag_font_version) + emoji_unicode_version = emoji_font_version_to_unicode_version(emoji_font_version) + errors = [] for sequence in all_emoji: if all([sequence not in coverage for coverage in coverages]): - errors.append('%s is not supported in the emoji font.' % printable(sequence)) + sequence_version = float(_age_by_chars[sequence]) + if is_flag_sequence(sequence): + if sequence_version <= emoji_flag_unicode_version: + errors.append('%s is not supported in the emoji font.' % printable(sequence)) + else: + if sequence_version <= emoji_unicode_version: + errors.append('%s is not supported in the emoji font.' % printable(sequence)) for coverage in coverages: for sequence in coverage: @@ -480,6 +527,19 @@ def check_emoji_defaults(default_emoji): repr(missing_text_chars)) +def parse_unicode_seq(chars): + if ' ' in chars: # character sequence + sequence = [int(ch, 16) for ch in chars.split(' ')] + additions = [tuple(sequence)] + elif '..' in chars: # character range + char_start, char_end = chars.split('..') + char_start = int(char_start, 16) + char_end = int(char_end, 16) + additions = range(char_start, char_end+1) + else: # single character + additions = [int(chars, 16)] + return additions + # Setting reverse to true returns a dictionary that maps the values to sets of # characters, useful for some binary properties. Otherwise, we get a # dictionary that maps characters to the property values, assuming there's only @@ -501,16 +561,8 @@ def parse_unicode_datafile(file_path, reverse=False): chars = chars.strip() prop = prop.strip() - if ' ' in chars: # character sequence - sequence = [int(ch, 16) for ch in chars.split(' ')] - additions = [tuple(sequence)] - elif '..' in chars: # character range - char_start, char_end = chars.split('..') - char_start = int(char_start, 16) - char_end = int(char_end, 16) - additions = range(char_start, char_end+1) - else: # singe character - additions = [int(chars, 16)] + additions = parse_unicode_seq(chars) + if reverse: output_dict[prop].update(additions) else: @@ -519,6 +571,32 @@ def parse_unicode_datafile(file_path, reverse=False): output_dict[addition] = prop return output_dict +def parse_sequence_age(file_path): + VERSION_RE = re.compile(r'E([\d\.]+)') + output_dict = {} + with open(file_path) as datafile: + for line in datafile: + comment = '' + if '#' in line: + hash_pos = line.index('#') + comment = line[hash_pos + 1:].strip() + line = line[:hash_pos] + line = line.strip() + if not line: + continue + + chars = line[:line.index(';')].strip() + + m = VERSION_RE.match(comment) + assert m, 'Version not found: unknown format: %s' % line + version = m.group(1) + + additions = parse_unicode_seq(chars) + + for addition in additions: + assert addition not in output_dict + output_dict[addition] = version + return output_dict def parse_emoji_variants(file_path): emoji_set = set() @@ -543,7 +621,7 @@ def parse_emoji_variants(file_path): def parse_ucd(ucd_path): - global _emoji_properties, _chars_by_age + global _emoji_properties, _chars_by_age, _age_by_chars global _text_variation_sequences, _emoji_variation_sequences global _emoji_sequences, _emoji_zwj_sequences _emoji_properties = parse_unicode_datafile( @@ -555,6 +633,10 @@ def parse_ucd(ucd_path): _chars_by_age = parse_unicode_datafile( path.join(ucd_path, 'DerivedAge.txt'), reverse=True) + _age_by_chars = parse_unicode_datafile( + path.join(ucd_path, 'DerivedAge.txt')) + _age_by_chars.update(parse_sequence_age( + path.join(ucd_path, 'emoji-sequences.txt'))) sequences = parse_emoji_variants( path.join(ucd_path, 'emoji-variation-sequences.txt')) _text_variation_sequences, _emoji_variation_sequences = sequences @@ -743,44 +825,12 @@ def check_cjk_punctuation(): break assert_font_supports_none_of_chars(record.font, cjk_punctuation, name) -def getPostScriptName(font): - font_file, index = font - font_path = path.join(_fonts_dir, font_file) - if index is not None: - # Use the first font file in the collection for resolving post script name. - ttf = ttLib.TTFont(font_path, fontNumber=0) - else: - ttf = ttLib.TTFont(font_path) - - nameTable = ttf['name'] - for name in nameTable.names: - if (name.nameID == 6 and name.platformID == 3 and name.platEncID == 1 - and name.langID == 0x0409): - return str(name) - -def check_canonical_name(): - for record in _all_fonts: - file_name, index = record.font - - psName = getPostScriptName(record.font) - if record.psName: - # If fonts element has postScriptName attribute, it should match with the PostScript - # name in the name table. - assert psName == record.psName, ('postScriptName attribute %s should match with %s' % ( - record.psName, psName)) - else: - # If fonts element doesn't have postScriptName attribute, the file name should match - # with the PostScript name in the name table. - assert psName == file_name[:-4], ('file name %s should match with %s' % ( - file_name, psName)) - - def main(): global _fonts_dir target_out = sys.argv[1] _fonts_dir = path.join(target_out, 'fonts') - fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml') + fonts_xml_path = path.join(target_out, 'etc', 'font_fallback.xml') parse_fonts_xml(fonts_xml_path) @@ -793,8 +843,6 @@ def main(): check_cjk_punctuation() - check_canonical_name() - check_emoji = sys.argv[2] if check_emoji == 'true': ucd_path = sys.argv[3] diff --git a/tools/lint/global/integration_tests/Android.bp b/tools/lint/global/integration_tests/Android.bp index 40281d263a4c..05ba405d2c52 100644 --- a/tools/lint/global/integration_tests/Android.bp +++ b/tools/lint/global/integration_tests/Android.bp @@ -38,7 +38,7 @@ java_library { python_library_host { name: "AndroidGlobalLintTestNoAidl_py", - data: [":AndroidGlobalLintTestNoAidl{.lint}"], + device_common_data: [":AndroidGlobalLintTestNoAidl{.lint}"], pkg_path: "no_aidl", } @@ -53,7 +53,7 @@ java_library { python_library_host { name: "AndroidGlobalLintTestMissingAnnotation_py", - data: [":AndroidGlobalLintTestMissingAnnotation{.lint}"], + device_common_data: [":AndroidGlobalLintTestMissingAnnotation{.lint}"], pkg_path: "missing_annotation", } diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp index 73caac694cb4..24ec12c50520 100644 --- a/tools/preload-check/Android.bp +++ b/tools/preload-check/Android.bp @@ -28,5 +28,5 @@ java_test_host { libs: ["tradefed"], test_suites: ["general-tests"], required: ["preload-check-device"], - data: [":preload-check-device"], + device_common_data: [":preload-check-device"], } |